Move to Zustand and add Filters to web_test (#1077)
* Moved to zustand and started refactored filters - Move from redux to zustand - Refactor Filters typings and add some helper types - Add Filters button and Picker/Switch filter options - Fixed Syosetu typings * Add rule consistent-type-definitions - Add consistent-type-definitions eslint rule to force consistent use of types instead of interfaces - Run eslint to auto-change every interface to types https://dev.to/atheodosiou/types-vs-interfaces-in-typescript-making-the-right-choice-48a4 https://www.youtube.com/watch?v=zM9UPcIyyhQ * Add other filters and refactor - Move Dialog to FiltersDialog - CheckboxFilter, TextFilter, XCheckboxFilter added - Added TripleCheckbox and TripleSwitch as tri-state checkbox/switch items using Mui stylings * TriStateCheckbox/Switch refactor - Change name of TripleSwitch and TripleCheckbox to TriState - Change slightly TriStateSwitch and StyledSwitch styles - Add option to change into TriStateSwitches via `localStorage` value `useSwitches` * Refactor code and fix issues * fix: prettier --------- Co-authored-by: nyagami <hoangquan05112002@gmail.com>
This commit is contained in:
@@ -13,3 +13,4 @@ src/plugins/*/*\[*\]*.ts
|
|||||||
dist
|
dist
|
||||||
total.svg
|
total.svg
|
||||||
src/plugins/index.ts
|
src/plugins/index.ts
|
||||||
|
.history
|
||||||
+3
-1
@@ -138,13 +138,14 @@ export default tseslint.config(
|
|||||||
'@typescript-eslint/no-unused-vars': 'warn',
|
'@typescript-eslint/no-unused-vars': 'warn',
|
||||||
'@typescript-eslint/no-explicit-any': 'warn',
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
'@typescript-eslint/ban-ts-comment': 'off',
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
'@typescript-eslint/consistent-type-definitions': ['error', 'type'],
|
||||||
'no-case-declarations': 'warn',
|
'no-case-declarations': 'warn',
|
||||||
'no-undef': 'error',
|
'no-undef': 'error',
|
||||||
},
|
},
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
ecmaVersion: 5,
|
ecmaVersion: 5,
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
globals: Object.assign(...globalsHermes),
|
globals: Object.assign({}, ...globalsHermes),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -154,6 +155,7 @@ export default tseslint.config(
|
|||||||
'no-unused-vars': 'off',
|
'no-unused-vars': 'off',
|
||||||
'@typescript-eslint/no-unused-vars': 'warn',
|
'@typescript-eslint/no-unused-vars': 'warn',
|
||||||
'@typescript-eslint/no-namespace': 'off',
|
'@typescript-eslint/no-namespace': 'off',
|
||||||
|
'@typescript-eslint/consistent-type-definitions': ['error', 'type'],
|
||||||
'@typescript-eslint/no-explicit-any': 'warn',
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
'@typescript-eslint/ban-ts-comment': 'off',
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
'no-undef': 'error',
|
'no-undef': 'error',
|
||||||
|
|||||||
Generated
+66
-1
@@ -26,11 +26,13 @@
|
|||||||
"react-virtuoso": "^4.7.11",
|
"react-virtuoso": "^4.7.11",
|
||||||
"react-window": "^1.8.10",
|
"react-window": "^1.8.10",
|
||||||
"sanitize-html": "^2.12.1",
|
"sanitize-html": "^2.12.1",
|
||||||
"urlencode": "^2.0.0"
|
"urlencode": "^2.0.0",
|
||||||
|
"zustand": "^4.5.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.5.0",
|
"@eslint/js": "^9.5.0",
|
||||||
"@types/eslint__js": "^8.42.3",
|
"@types/eslint__js": "^8.42.3",
|
||||||
|
"@types/eslint-config-prettier": "^6.11.3",
|
||||||
"@types/qs": "^6.9.15",
|
"@types/qs": "^6.9.15",
|
||||||
"@types/react": "^18.2.66",
|
"@types/react": "^18.2.66",
|
||||||
"@types/react-dom": "^18.2.22",
|
"@types/react-dom": "^18.2.22",
|
||||||
@@ -1868,6 +1870,12 @@
|
|||||||
"@types/eslint": "*"
|
"@types/eslint": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/eslint-config-prettier": {
|
||||||
|
"version": "6.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/eslint-config-prettier/-/eslint-config-prettier-6.11.3.tgz",
|
||||||
|
"integrity": "sha512-3wXCiM8croUnhg9LdtZUJQwNcQYGWxxdOWDjPe1ykCqJFPVpzAKfs/2dgSoCtAvdPeaponcWPI7mPcGGp9dkKQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||||
@@ -5661,6 +5669,41 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zustand": {
|
||||||
|
"version": "4.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.4.tgz",
|
||||||
|
"integrity": "sha512-/BPMyLKJPtFEvVL0E9E9BTUM63MNyhPGlvxk1XjrfWTUlV+BR8jufjsovHzrtR6YNcBEcL7cMHovL1n9xHawEg==",
|
||||||
|
"dependencies": {
|
||||||
|
"use-sync-external-store": "1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.7.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=16.8",
|
||||||
|
"immer": ">=9.0.6",
|
||||||
|
"react": ">=16.8"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"immer": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zustand/node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -6763,6 +6806,12 @@
|
|||||||
"@types/eslint": "*"
|
"@types/eslint": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/eslint-config-prettier": {
|
||||||
|
"version": "6.11.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/eslint-config-prettier/-/eslint-config-prettier-6.11.3.tgz",
|
||||||
|
"integrity": "sha512-3wXCiM8croUnhg9LdtZUJQwNcQYGWxxdOWDjPe1ykCqJFPVpzAKfs/2dgSoCtAvdPeaponcWPI7mPcGGp9dkKQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/estree": {
|
"@types/estree": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||||
@@ -9390,6 +9439,22 @@
|
|||||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||||
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
|
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
|
},
|
||||||
|
"zustand": {
|
||||||
|
"version": "4.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.4.tgz",
|
||||||
|
"integrity": "sha512-/BPMyLKJPtFEvVL0E9E9BTUM63MNyhPGlvxk1XjrfWTUlV+BR8jufjsovHzrtR6YNcBEcL7cMHovL1n9xHawEg==",
|
||||||
|
"requires": {
|
||||||
|
"use-sync-external-store": "1.2.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"use-sync-external-store": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||||
|
"requires": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -39,11 +39,13 @@
|
|||||||
"react-virtuoso": "^4.7.11",
|
"react-virtuoso": "^4.7.11",
|
||||||
"react-window": "^1.8.10",
|
"react-window": "^1.8.10",
|
||||||
"sanitize-html": "^2.12.1",
|
"sanitize-html": "^2.12.1",
|
||||||
"urlencode": "^2.0.0"
|
"urlencode": "^2.0.0",
|
||||||
|
"zustand": "^4.5.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.5.0",
|
"@eslint/js": "^9.5.0",
|
||||||
"@types/eslint__js": "^8.42.3",
|
"@types/eslint__js": "^8.42.3",
|
||||||
|
"@types/eslint-config-prettier": "^6.11.3",
|
||||||
"@types/qs": "^6.9.15",
|
"@types/qs": "^6.9.15",
|
||||||
"@types/react": "^18.2.66",
|
"@types/react": "^18.2.66",
|
||||||
"@types/react-dom": "^18.2.22",
|
"@types/react-dom": "^18.2.22",
|
||||||
|
|||||||
+2
-1
@@ -26,6 +26,7 @@ const proxyRequest = (req, res) => {
|
|||||||
|
|
||||||
proxy.on('proxyRes', function (proxyRes, req, res) {
|
proxy.on('proxyRes', function (proxyRes, req, res) {
|
||||||
var statusCode = proxyRes.statusCode;
|
var statusCode = proxyRes.statusCode;
|
||||||
|
// Redirect
|
||||||
if (
|
if (
|
||||||
statusCode === 301 ||
|
statusCode === 301 ||
|
||||||
statusCode === 302 ||
|
statusCode === 302 ||
|
||||||
@@ -82,7 +83,7 @@ const cookiesHandler = (req, res) => {
|
|||||||
|
|
||||||
http
|
http
|
||||||
.createServer(function (req, res) {
|
.createServer(function (req, res) {
|
||||||
const path = req.url.replace(/^\//, '');
|
const path = req.url.charAt(0) === '/' ? req.url.slice(1) : req.url;
|
||||||
if (req.headers['access-control-request-method']) {
|
if (req.headers['access-control-request-method']) {
|
||||||
res.setHeader(
|
res.setHeader(
|
||||||
'access-control-allow-methods',
|
'access-control-allow-methods',
|
||||||
|
|||||||
@@ -55,16 +55,16 @@ async function getFilters(sources: HotNovelPubMetadata) {
|
|||||||
return filters;
|
return filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface response {
|
type response = {
|
||||||
status: number;
|
status: number;
|
||||||
message: string;
|
message: string;
|
||||||
data?: DataEntity[];
|
data?: DataEntity[];
|
||||||
}
|
};
|
||||||
interface DataEntity {
|
type DataEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
async function start() {
|
async function start() {
|
||||||
const result = [];
|
const result = [];
|
||||||
|
|||||||
@@ -3,17 +3,17 @@ import { Filters, FilterTypes } from '@libs/filterInputs';
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
|
|
||||||
export interface HotNovelPubMetadata {
|
export type HotNovelPubMetadata = {
|
||||||
id: string;
|
id: string;
|
||||||
sourceSite: string;
|
sourceSite: string;
|
||||||
sourceName: string;
|
sourceName: string;
|
||||||
filters?: Filters;
|
filters?: Filters;
|
||||||
options?: HotNovelPubOptions;
|
options?: HotNovelPubOptions;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface HotNovelPubOptions {
|
type HotNovelPubOptions = {
|
||||||
lang?: string;
|
lang?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
class HotNovelPubPlugin implements Plugin.PluginBase {
|
class HotNovelPubPlugin implements Plugin.PluginBase {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -161,16 +161,16 @@ class HotNovelPubPlugin implements Plugin.PluginBase {
|
|||||||
resolveUrl = (path: string) => this.site + '/' + path;
|
resolveUrl = (path: string) => this.site + '/' + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface responseNovels {
|
type responseNovels = {
|
||||||
status: number;
|
status: number;
|
||||||
message: string;
|
message: string;
|
||||||
data: Data;
|
data: Data;
|
||||||
}
|
};
|
||||||
interface Data {
|
type Data = {
|
||||||
category?: Category;
|
category?: Category;
|
||||||
books: Books;
|
books: Books;
|
||||||
}
|
};
|
||||||
interface Category {
|
type Category = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
@@ -178,13 +178,13 @@ interface Category {
|
|||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
countryId: number;
|
countryId: number;
|
||||||
}
|
};
|
||||||
interface Books {
|
type Books = {
|
||||||
total: number;
|
total: number;
|
||||||
pages_count: number;
|
pages_count: number;
|
||||||
data?: DataEntity[];
|
data?: DataEntity[];
|
||||||
}
|
};
|
||||||
interface DataEntity {
|
type DataEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
view: number;
|
view: number;
|
||||||
@@ -193,24 +193,24 @@ interface DataEntity {
|
|||||||
slug: string;
|
slug: string;
|
||||||
categories?: CategoriesEntity[] | null;
|
categories?: CategoriesEntity[] | null;
|
||||||
source?: null;
|
source?: null;
|
||||||
}
|
};
|
||||||
interface CategoriesEntity {
|
type CategoriesEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
slug?: string;
|
slug?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseNovel {
|
type responseNovel = {
|
||||||
status: number;
|
status: number;
|
||||||
message: string;
|
message: string;
|
||||||
data: Data1;
|
data: Data1;
|
||||||
}
|
};
|
||||||
interface Data1 {
|
type Data1 = {
|
||||||
book: Book1;
|
book: Book1;
|
||||||
tags: Tags;
|
tags: Tags;
|
||||||
chapters?: ChaptersEntity[];
|
chapters?: ChaptersEntity[];
|
||||||
}
|
};
|
||||||
interface Book1 {
|
type Book1 = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
@@ -229,47 +229,47 @@ interface Book1 {
|
|||||||
source?: null;
|
source?: null;
|
||||||
idSource?: null;
|
idSource?: null;
|
||||||
prize?: null;
|
prize?: null;
|
||||||
}
|
};
|
||||||
interface Authorize {
|
type Authorize = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
}
|
};
|
||||||
interface Tags {
|
type Tags = {
|
||||||
tags_name?: string[] | null;
|
tags_name?: string[] | null;
|
||||||
tags?: CategoriesEntity[] | null;
|
tags?: CategoriesEntity[] | null;
|
||||||
}
|
};
|
||||||
interface ChaptersEntity {
|
type ChaptersEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
index: number;
|
index: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ChapterType {
|
type ChapterType = {
|
||||||
type: string;
|
type: string;
|
||||||
data?: string[];
|
data?: string[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseSearch {
|
type responseSearch = {
|
||||||
status: number;
|
status: number;
|
||||||
message: string;
|
message: string;
|
||||||
data: Data2;
|
data: Data2;
|
||||||
}
|
};
|
||||||
interface Data2 {
|
type Data2 = {
|
||||||
books?: BooksEntity[] | null;
|
books?: BooksEntity[] | null;
|
||||||
authorizes?: AuthorizesEntityOrCategoriesEntity[] | null;
|
authorizes?: AuthorizesEntityOrCategoriesEntity[] | null;
|
||||||
categories?: AuthorizesEntityOrCategoriesEntity[] | null;
|
categories?: AuthorizesEntityOrCategoriesEntity[] | null;
|
||||||
}
|
};
|
||||||
interface BooksEntity {
|
type BooksEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
}
|
};
|
||||||
interface AuthorizesEntityOrCategoriesEntity {
|
type AuthorizesEntityOrCategoriesEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import { NovelStatus } from '@libs/novelStatus';
|
|||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
export interface IfreedomMetadata {
|
export type IfreedomMetadata = {
|
||||||
id: string;
|
id: string;
|
||||||
sourceSite: string;
|
sourceSite: string;
|
||||||
sourceName: string;
|
sourceName: string;
|
||||||
filters?: Filters;
|
filters?: Filters;
|
||||||
}
|
};
|
||||||
|
|
||||||
class IfreedomPlugin implements Plugin.PluginBase {
|
class IfreedomPlugin implements Plugin.PluginBase {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -4,18 +4,18 @@ import { Plugin } from '@typings/plugin';
|
|||||||
import { Filters, FilterTypes } from '@libs/filterInputs';
|
import { Filters, FilterTypes } from '@libs/filterInputs';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
interface LightNovelWorldOptions {
|
type LightNovelWorldOptions = {
|
||||||
lang?: string;
|
lang?: string;
|
||||||
versionIncrements?: number;
|
versionIncrements?: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface LightNovelWorldMetadata {
|
export type LightNovelWorldMetadata = {
|
||||||
id: string;
|
id: string;
|
||||||
sourceSite: string;
|
sourceSite: string;
|
||||||
sourceName: string;
|
sourceName: string;
|
||||||
options?: LightNovelWorldOptions;
|
options?: LightNovelWorldOptions;
|
||||||
filters?: any;
|
filters?: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
class LightNovelWorld implements Plugin.PagePlugin {
|
class LightNovelWorld implements Plugin.PagePlugin {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -6,21 +6,21 @@ import { NovelStatus } from '@libs/novelStatus';
|
|||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { Filters } from '@libs/filterInputs';
|
import { Filters } from '@libs/filterInputs';
|
||||||
|
|
||||||
interface LightNovelWPOptions {
|
type LightNovelWPOptions = {
|
||||||
reverseChapters?: boolean;
|
reverseChapters?: boolean;
|
||||||
lang?: string;
|
lang?: string;
|
||||||
versionIncrements?: number;
|
versionIncrements?: number;
|
||||||
seriesPath?: string;
|
seriesPath?: string;
|
||||||
customJs?: string;
|
customJs?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface LightNovelWPMetadata {
|
export type LightNovelWPMetadata = {
|
||||||
id: string;
|
id: string;
|
||||||
sourceSite: string;
|
sourceSite: string;
|
||||||
sourceName: string;
|
sourceName: string;
|
||||||
options?: LightNovelWPOptions;
|
options?: LightNovelWPOptions;
|
||||||
filters?: any;
|
filters?: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
class LightNovelWPPlugin implements Plugin.PluginBase {
|
class LightNovelWPPlugin implements Plugin.PluginBase {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -9,20 +9,20 @@ import dayjs from 'dayjs';
|
|||||||
const includesAny = (str: string, keywords: string[]) =>
|
const includesAny = (str: string, keywords: string[]) =>
|
||||||
new RegExp(keywords.join('|')).test(str);
|
new RegExp(keywords.join('|')).test(str);
|
||||||
|
|
||||||
interface MadaraOptions {
|
type MadaraOptions = {
|
||||||
useNewChapterEndpoint?: boolean;
|
useNewChapterEndpoint?: boolean;
|
||||||
lang?: string;
|
lang?: string;
|
||||||
orderBy?: string;
|
orderBy?: string;
|
||||||
versionIncrements?: number;
|
versionIncrements?: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface MadaraMetadata {
|
export type MadaraMetadata = {
|
||||||
id: string;
|
id: string;
|
||||||
sourceSite: string;
|
sourceSite: string;
|
||||||
sourceName: string;
|
sourceName: string;
|
||||||
options?: MadaraOptions;
|
options?: MadaraOptions;
|
||||||
filters?: any;
|
filters?: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
class MadaraPlugin implements Plugin.PluginBase {
|
class MadaraPlugin implements Plugin.PluginBase {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -5,17 +5,17 @@ import { NovelStatus } from '@libs/novelStatus';
|
|||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { Filters } from '@libs/filterInputs';
|
import { Filters } from '@libs/filterInputs';
|
||||||
|
|
||||||
interface MTLNovelOptions {
|
type MTLNovelOptions = {
|
||||||
lang?: string;
|
lang?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface MTLNovelMetadata {
|
export type MTLNovelMetadata = {
|
||||||
id: string;
|
id: string;
|
||||||
sourceSite: string;
|
sourceSite: string;
|
||||||
sourceName: string;
|
sourceName: string;
|
||||||
options?: MTLNovelOptions;
|
options?: MTLNovelOptions;
|
||||||
filters?: any;
|
filters?: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
class MTLNovelPlugin implements Plugin.PluginBase {
|
class MTLNovelPlugin implements Plugin.PluginBase {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -197,11 +197,11 @@ class MTLNovelPlugin implements Plugin.PluginBase {
|
|||||||
const result = await res.json();
|
const result = await res.json();
|
||||||
|
|
||||||
const novels: Plugin.NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
interface SearchEntry {
|
type SearchEntry = {
|
||||||
title: string;
|
title: string;
|
||||||
thumbnail: string;
|
thumbnail: string;
|
||||||
permalink: string;
|
permalink: string;
|
||||||
}
|
};
|
||||||
result.items[0].results.map((item: SearchEntry) => {
|
result.items[0].results.map((item: SearchEntry) => {
|
||||||
const novelName = item.title.replace(/<\/?strong>/g, '');
|
const novelName = item.title.replace(/<\/?strong>/g, '');
|
||||||
const novelCover = item.thumbnail;
|
const novelCover = item.thumbnail;
|
||||||
|
|||||||
@@ -3,18 +3,18 @@ import { fetchApi } from '@libs/fetch';
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
|
|
||||||
interface ReadNovelFullOptions {
|
type ReadNovelFullOptions = {
|
||||||
lang?: string;
|
lang?: string;
|
||||||
versionIncrements?: number;
|
versionIncrements?: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface ReadNovelFullMetadata {
|
export type ReadNovelFullMetadata = {
|
||||||
id: string;
|
id: string;
|
||||||
sourceSite: string;
|
sourceSite: string;
|
||||||
sourceName: string;
|
sourceName: string;
|
||||||
options?: ReadNovelFullOptions;
|
options?: ReadNovelFullOptions;
|
||||||
filters?: any;
|
filters?: any;
|
||||||
}
|
};
|
||||||
|
|
||||||
class ReadNovelFullPlugin implements Plugin.PluginBase {
|
class ReadNovelFullPlugin implements Plugin.PluginBase {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ import { load as parseHTML } from 'cheerio';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import qs from 'qs';
|
import qs from 'qs';
|
||||||
|
|
||||||
interface ReadwnOptions {
|
type ReadwnOptions = {
|
||||||
versionIncrements?: number;
|
versionIncrements?: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface ReadwnMetadata {
|
export type ReadwnMetadata = {
|
||||||
id: string;
|
id: string;
|
||||||
sourceSite: string;
|
sourceSite: string;
|
||||||
sourceName: string;
|
sourceName: string;
|
||||||
filters?: any;
|
filters?: any;
|
||||||
options?: ReadwnOptions;
|
options?: ReadwnOptions;
|
||||||
}
|
};
|
||||||
|
|
||||||
class ReadwnPlugin implements Plugin.PluginBase {
|
class ReadwnPlugin implements Plugin.PluginBase {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import { NovelStatus } from '@libs/novelStatus';
|
|||||||
import { load as parseHTML } from 'cheerio';
|
import { load as parseHTML } from 'cheerio';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
export interface RulateMetadata {
|
export type RulateMetadata = {
|
||||||
id: string;
|
id: string;
|
||||||
sourceSite: string;
|
sourceSite: string;
|
||||||
sourceName: string;
|
sourceName: string;
|
||||||
filters?: Filters;
|
filters?: Filters;
|
||||||
}
|
};
|
||||||
|
|
||||||
class RulatePlugin implements Plugin.PluginBase {
|
class RulatePlugin implements Plugin.PluginBase {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -241,10 +241,10 @@ class RulatePlugin implements Plugin.PluginBase {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface response {
|
type response = {
|
||||||
id: number;
|
id: number;
|
||||||
title_one: string;
|
title_one: string;
|
||||||
title_two: string;
|
title_two: string;
|
||||||
url: string;
|
url: string;
|
||||||
img: string;
|
img: string;
|
||||||
}
|
};
|
||||||
|
|||||||
+19
-23
@@ -8,8 +8,6 @@ import {
|
|||||||
ThemeProvider,
|
ThemeProvider,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import Appbar from '@components/Appbar';
|
import Appbar from '@components/Appbar';
|
||||||
import { Provider } from 'react-redux';
|
|
||||||
import store from '@redux/store';
|
|
||||||
import AppProvider from './provider';
|
import AppProvider from './provider';
|
||||||
import PopularNovels from '@components/accordions/PopularNovels';
|
import PopularNovels from '@components/accordions/PopularNovels';
|
||||||
import SearchNovels from '@components/accordions/SearchNovels';
|
import SearchNovels from '@components/accordions/SearchNovels';
|
||||||
@@ -30,27 +28,25 @@ function App() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<StyledEngineProvider injectFirst>
|
||||||
<StyledEngineProvider injectFirst>
|
<ThemeProvider theme={theme}>
|
||||||
<ThemeProvider theme={theme}>
|
<CssBaseline />
|
||||||
<CssBaseline />
|
<Appbar />
|
||||||
<Appbar />
|
<Container
|
||||||
<Container
|
sx={{
|
||||||
sx={{
|
mt: 6,
|
||||||
mt: 6,
|
width: { sm: 500, md: 1024, lg: '90vw' },
|
||||||
width: { sm: 500, md: 1024, lg: '90vw' },
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<HeadersSection />
|
||||||
<HeadersSection />
|
<PopularNovels />
|
||||||
<PopularNovels />
|
<SearchNovels />
|
||||||
<SearchNovels />
|
<ParseNovel />
|
||||||
<ParseNovel />
|
<ParseChapter />
|
||||||
<ParseChapter />
|
</Container>
|
||||||
</Container>
|
<AppProvider />
|
||||||
<AppProvider />
|
</ThemeProvider>
|
||||||
</ThemeProvider>
|
</StyledEngineProvider>
|
||||||
</StyledEngineProvider>
|
|
||||||
</Provider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,42 +18,29 @@ import { Search, Translate } from '@mui/icons-material';
|
|||||||
import { searchPlugins } from '@provider/plugins';
|
import { searchPlugins } from '@provider/plugins';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { FixedSizeList, ListChildComponentProps } from 'react-window';
|
import { FixedSizeList, ListChildComponentProps } from 'react-window';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useAppStore } from '@store';
|
||||||
import { selectPlugin } from '@redux/pluginSlice';
|
|
||||||
import usePlugin from '@hooks/usePlugin';
|
|
||||||
|
|
||||||
const resovleIcon = (iconPath: string) => {
|
const resovleIcon = (iconPath: string) => {
|
||||||
return '/static/' + iconPath;
|
return '/static/' + iconPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function () {
|
function renderPlugin(setListVisible: (v: boolean) => void) {
|
||||||
const [searchedPlugins, setSearchPlugins] = useState<Plugin.PluginBase[]>([]);
|
return function (props: ListChildComponentProps<Plugin.PluginBase[]>) {
|
||||||
const [keyword, setKeyword] = useState('');
|
|
||||||
const [listVisible, setListVisible] = useState(false);
|
|
||||||
const plugin = usePlugin();
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
useEffect(() => {
|
|
||||||
const plugins = searchPlugins(keyword);
|
|
||||||
setSearchPlugins(plugins);
|
|
||||||
}, [keyword]);
|
|
||||||
|
|
||||||
function renderPlugin(props: ListChildComponentProps<Plugin.PluginBase[]>) {
|
|
||||||
const { index, style, data } = props;
|
const { index, style, data } = props;
|
||||||
const plugin = data[index];
|
const plugin = data[index];
|
||||||
|
const selectPlugin = useAppStore(state => state.selectPlugin);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem style={style} key={index} component="div" disablePadding>
|
<ListItem style={style} key={index} component="div" disablePadding>
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch(
|
selectPlugin({
|
||||||
selectPlugin({
|
id: plugin.id,
|
||||||
id: plugin.id,
|
name: plugin.name,
|
||||||
name: plugin.name,
|
version: plugin.version,
|
||||||
version: plugin.version,
|
icon: plugin.icon,
|
||||||
icon: plugin.icon,
|
site: plugin.site,
|
||||||
site: plugin.site,
|
});
|
||||||
}),
|
|
||||||
);
|
|
||||||
setListVisible(false);
|
setListVisible(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -64,7 +51,20 @@ export default function () {
|
|||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Appbar() {
|
||||||
|
const [searchedPlugins, setSearchPlugins] = useState<Plugin.PluginBase[]>([]);
|
||||||
|
const [keyword, setKeyword] = useState('');
|
||||||
|
const [listVisible, setListVisible] = useState(false);
|
||||||
|
|
||||||
|
const plugin = useAppStore(state => state.plugin);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const plugins = searchPlugins(keyword);
|
||||||
|
setSearchPlugins(plugins);
|
||||||
|
}, [keyword]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: 'flex' }}>
|
<Box sx={{ display: 'flex' }}>
|
||||||
@@ -111,7 +111,7 @@ export default function () {
|
|||||||
backgroundColor: 'white',
|
backgroundColor: 'white',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{renderPlugin}
|
{renderPlugin(setListVisible)}
|
||||||
</FixedSizeList>
|
</FixedSizeList>
|
||||||
) : null}
|
) : null}
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { createTheme, ThemeProvider } from '@mui/material/styles';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import CopiableItem from './CopiableItem';
|
import CopiableItem from './CopiableItem';
|
||||||
|
|
||||||
interface Validation {
|
type Validation = {
|
||||||
type?: 'error' | 'warning';
|
type?: 'error' | 'warning';
|
||||||
message?: string;
|
message?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
const validatePath = (path: string): Validation => {
|
const validatePath = (path: string): Validation => {
|
||||||
if (path.includes('http'))
|
if (path.includes('http'))
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { styled, Switch } from '@mui/material';
|
||||||
|
|
||||||
|
export const StyledSwitch = styled(Switch)(() => ({
|
||||||
|
padding: 8,
|
||||||
|
'& .MuiSwitch-track': {
|
||||||
|
borderRadius: 22 / 2,
|
||||||
|
},
|
||||||
|
'& .MuiSwitch-thumb': {
|
||||||
|
boxShadow: 'none',
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
margin: 2,
|
||||||
|
},
|
||||||
|
}));
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Box, Button, Stack, TextField } from '@mui/material';
|
import { Box, Button, Stack, TextField } from '@mui/material';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import AccordionContainer from '../components/AccordionContainer';
|
import AccordionContainer from '../components/AccordionContainer';
|
||||||
import usePlugin from '@hooks/usePlugin';
|
|
||||||
import Textarea from '@components/Textarea';
|
import Textarea from '@components/Textarea';
|
||||||
|
import { useAppStore } from '@store';
|
||||||
export default function ParseChapter() {
|
export default function ParseChapter() {
|
||||||
const plugin = usePlugin();
|
const plugin = useAppStore(state => state.plugin);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [chapterPath, setchapterPath] = useState('');
|
const [chapterPath, setchapterPath] = useState('');
|
||||||
const [chapterText, setChapterText] = useState('');
|
const [chapterText, setChapterText] = useState('');
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { Box, Button, Pagination, Stack, TextField } from '@mui/material';
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import AccordionContainer from '../components/AccordionContainer';
|
import AccordionContainer from '../components/AccordionContainer';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import usePlugin from '@hooks/usePlugin';
|
|
||||||
import SourceNovelCard from './SourceNovelCard';
|
import SourceNovelCard from './SourceNovelCard';
|
||||||
import ChapterList from './ChaptertList';
|
import ChapterList from './ChaptertList';
|
||||||
|
import { useAppStore } from '@store';
|
||||||
|
|
||||||
export default function ParseNovel() {
|
export default function ParseNovel() {
|
||||||
const plugin = usePlugin();
|
const plugin = useAppStore(state => state.plugin);
|
||||||
const [sourceNovel, setSouceNovel] = useState<
|
const [sourceNovel, setSouceNovel] = useState<
|
||||||
Plugin.SourceNovel | undefined
|
Plugin.SourceNovel | undefined
|
||||||
>();
|
>();
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#filtersContent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch_filter {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
@@ -8,40 +8,42 @@ import {
|
|||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import React, { useEffect, useState, ChangeEvent } from 'react';
|
import React, { useEffect, useState, ChangeEvent, useRef } from 'react';
|
||||||
import AccordionContainer from '../components/AccordionContainer';
|
import AccordionContainer from '../components/AccordionContainer';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import NovelItemCard from '../components/NovelItemCard';
|
import NovelItemCard from '../components/NovelItemCard';
|
||||||
import { Filters } from '@libs/filterInputs';
|
import { Filters, FilterToValues } from '@libs/filterInputs';
|
||||||
import usePlugin from '@hooks/usePlugin';
|
import { useAppStore } from '@store';
|
||||||
|
import './index.css';
|
||||||
|
import { DialogRef, FiltersDialog } from '../components/filters/FiltersDialog';
|
||||||
|
|
||||||
export default function PopularNovels() {
|
export default function PopularNovels() {
|
||||||
const plugin = usePlugin();
|
const plugin = useAppStore(state => state.plugin);
|
||||||
const [novels, setNovels] = useState<Plugin.NovelItem[]>([]);
|
const [novels, setNovels] = useState<Plugin.NovelItem[]>([]);
|
||||||
const [filterValues, setFilterValues] = useState<Filters | undefined>();
|
const [filterValues, setFilterValues] = useState<
|
||||||
|
FilterToValues<Filters> | undefined
|
||||||
|
>();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [currentIndex, setCurrentIndex] = useState(0);
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
const [maxIndex, setMaxIndex] = useState(0);
|
const [maxIndex, setMaxIndex] = useState(0);
|
||||||
const [isLatest, setIsLatest] = useState(true);
|
const [isLatest, setIsLatest] = useState(true);
|
||||||
|
const dialogRef = useRef<DialogRef>(null);
|
||||||
|
|
||||||
const fetchNovelsByIndex = (index: number) => {
|
const fetchNovelsByIndex = async (index: number) => {
|
||||||
if (plugin && index) {
|
if (plugin && index) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
plugin
|
const novels = await plugin.popularNovels(index, {
|
||||||
.popularNovels(index, {
|
filters: filterValues || {},
|
||||||
filters: filterValues,
|
showLatestNovels: isLatest,
|
||||||
showLatestNovels: isLatest,
|
});
|
||||||
})
|
if (novels.length !== 0) {
|
||||||
.then(res => {
|
setCurrentIndex(index);
|
||||||
if (res.length !== 0) {
|
if (index > maxIndex) {
|
||||||
setCurrentIndex(index);
|
setMaxIndex(index);
|
||||||
if (index > maxIndex) {
|
}
|
||||||
setMaxIndex(index);
|
setNovels(novels);
|
||||||
}
|
}
|
||||||
setNovels(res);
|
setLoading(false);
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => setLoading(false));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,14 +61,15 @@ export default function PopularNovels() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Reset when changing plugins.
|
// Reset when changing plugins.
|
||||||
|
// TODO: Fix crashing on plugin change!
|
||||||
setCurrentIndex(0);
|
setCurrentIndex(0);
|
||||||
setMaxIndex(0);
|
setMaxIndex(0);
|
||||||
setNovels([]);
|
setNovels([]);
|
||||||
|
|
||||||
if (plugin?.filters) {
|
if (plugin?.filters) {
|
||||||
const filters = {};
|
const filters: FilterToValues<typeof plugin.filters> = {};
|
||||||
for (const fKey in plugin.filters) {
|
for (const fKey in plugin.filters) {
|
||||||
filters[fKey] = {
|
filters[fKey as keyof typeof filters] = {
|
||||||
type: plugin.filters[fKey].type,
|
type: plugin.filters[fKey].type,
|
||||||
value: plugin.filters[fKey].value,
|
value: plugin.filters[fKey].value,
|
||||||
};
|
};
|
||||||
@@ -74,6 +77,7 @@ export default function PopularNovels() {
|
|||||||
setFilterValues(filters);
|
setFilterValues(filters);
|
||||||
}
|
}
|
||||||
}, [plugin]);
|
}, [plugin]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AccordionContainer title="Popular Novels" loading={loading}>
|
<AccordionContainer title="Popular Novels" loading={loading}>
|
||||||
<Stack direction={'row'} spacing={2} alignItems={'center'}>
|
<Stack direction={'row'} spacing={2} alignItems={'center'}>
|
||||||
@@ -103,10 +107,25 @@ export default function PopularNovels() {
|
|||||||
>
|
>
|
||||||
Next Page
|
Next Page
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
disabled={!plugin || isLatest || !plugin.filters}
|
||||||
|
onClick={() => {
|
||||||
|
dialogRef.current?.show();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Filters
|
||||||
|
</Button>
|
||||||
|
<FiltersDialog
|
||||||
|
ref={dialogRef}
|
||||||
|
values={filterValues}
|
||||||
|
filters={plugin?.filters}
|
||||||
|
setValues={setFilterValues}
|
||||||
|
refetch={() => fetchNovelsByIndex(1)}
|
||||||
|
/>
|
||||||
<ToggleButtonGroup
|
<ToggleButtonGroup
|
||||||
value={currentIndex}
|
value={currentIndex}
|
||||||
exclusive
|
exclusive
|
||||||
onChange={(event, value) => fetchNovelsByIndex(value)}
|
onChange={(_event, value) => fetchNovelsByIndex(value)}
|
||||||
>
|
>
|
||||||
{Array.from({ length: maxIndex }, (_, index) => index + 1).map(
|
{Array.from({ length: maxIndex }, (_, index) => index + 1).map(
|
||||||
number => (
|
number => (
|
||||||
|
|||||||
@@ -3,35 +3,34 @@ import React, { useEffect, useState } from 'react';
|
|||||||
import AccordionContainer from '../components/AccordionContainer';
|
import AccordionContainer from '../components/AccordionContainer';
|
||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import NovelItemCard from '../components/NovelItemCard';
|
import NovelItemCard from '../components/NovelItemCard';
|
||||||
import { useSelector } from 'react-redux';
|
import { useAppStore } from '@store';
|
||||||
import { AppState } from '@redux/store';
|
|
||||||
import { getPlugin } from '@provider/plugins';
|
|
||||||
|
|
||||||
export default function SearchNovels() {
|
export default function SearchNovels() {
|
||||||
const selectedPluginItem = useSelector(
|
const plugin = useAppStore(state => state.plugin);
|
||||||
(state: AppState) => state.plugin.selected,
|
|
||||||
);
|
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [page] = useState(1);
|
const [page] = useState(1);
|
||||||
const [plugin, setPlugin] = useState<Plugin.PluginBase | undefined>();
|
|
||||||
const [novels, setNovels] = useState<Plugin.NovelItem[]>([]);
|
const [novels, setNovels] = useState<Plugin.NovelItem[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
useEffect(() => {
|
|
||||||
if (selectedPluginItem) {
|
const [fetchError, setFetchError] = useState('');
|
||||||
setPlugin(getPlugin(selectedPluginItem.id));
|
|
||||||
}
|
|
||||||
}, [selectedPluginItem]);
|
|
||||||
const fetchNovels = () => {
|
const fetchNovels = () => {
|
||||||
if (plugin) {
|
if (plugin) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
plugin
|
plugin
|
||||||
.searchNovels(searchTerm, page)
|
.searchNovels(searchTerm, page)
|
||||||
.then(res => setNovels(res))
|
.then(res => setNovels(res))
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false))
|
||||||
|
.catch(e => {
|
||||||
|
setFetchError(e.message);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AccordionContainer title="Search Novels" loading={loading}>
|
<AccordionContainer title="Search Novels" loading={loading}>
|
||||||
|
{fetchError && <span style={{ color: 'red' }}>{fetchError}</span>}
|
||||||
<Stack direction={'row'} spacing={2}>
|
<Stack direction={'row'} spacing={2}>
|
||||||
<TextField
|
<TextField
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Filter, FilterTypes, ValueOfFilter } from '@libs/filterInputs';
|
||||||
|
import {
|
||||||
|
Checkbox,
|
||||||
|
FormControl,
|
||||||
|
FormControlLabel,
|
||||||
|
FormLabel,
|
||||||
|
Grid,
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
|
type FilterType = FilterTypes.CheckboxGroup;
|
||||||
|
|
||||||
|
export const CheckboxFilter = ({
|
||||||
|
filter: { key, filter },
|
||||||
|
value,
|
||||||
|
set,
|
||||||
|
}: {
|
||||||
|
filter: { key: string; filter: Filter<FilterType> };
|
||||||
|
value: ValueOfFilter<FilterType>;
|
||||||
|
set: (v: typeof value) => void;
|
||||||
|
}) => {
|
||||||
|
const label = filter.label.replace(/s+/gi, '-');
|
||||||
|
return (
|
||||||
|
<FormControl key={key} variant="outlined">
|
||||||
|
<FormLabel>{filter.label}</FormLabel>
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
columns={{ xs: 3 }}
|
||||||
|
id={`checkbox_filter_${key}_${label}`}
|
||||||
|
>
|
||||||
|
{filter.options.map(option => {
|
||||||
|
return (
|
||||||
|
<Grid
|
||||||
|
key={`${key}_${option.label}`}
|
||||||
|
id={`checkbox_filter_option_${key}_${option.value.replace(/\s+/gi, '-')}`}
|
||||||
|
item
|
||||||
|
xs={1}
|
||||||
|
justifyContent={'center'}
|
||||||
|
display={'flex'}
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
onChange={() => {
|
||||||
|
set(
|
||||||
|
value.includes(option.value)
|
||||||
|
? value.filter(q => q !== option.value)
|
||||||
|
: [...value, option.value],
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
checked={value.includes(option.value)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={option.label}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
import React, {
|
||||||
|
forwardRef,
|
||||||
|
ReactNode,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
} from 'react';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@mui/material';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import {
|
||||||
|
AnyFilterValue,
|
||||||
|
Filters,
|
||||||
|
FilterToValues,
|
||||||
|
FilterTypes,
|
||||||
|
} from '@libs/filterInputs';
|
||||||
|
import { PickerFilter } from './PickerFilter';
|
||||||
|
import { SwitchFilter } from './SwitchFilter';
|
||||||
|
import { TextFilter } from './TextFilter';
|
||||||
|
import { CheckboxFilter } from './CheckboxFilter';
|
||||||
|
import { XCheckboxFilter } from './XCheckboxFilter';
|
||||||
|
|
||||||
|
const renderFilters = (
|
||||||
|
filters: Filters | undefined,
|
||||||
|
values: FilterToValues<Filters> | undefined,
|
||||||
|
set: (key: string, v: AnyFilterValue) => void,
|
||||||
|
): React.ReactNode => {
|
||||||
|
const isValueCorrectType = <T extends AnyFilterValue>(
|
||||||
|
o: AnyFilterValue,
|
||||||
|
f: T,
|
||||||
|
): o is T => {
|
||||||
|
const checkIfIsCorrectObjectType = (o: AnyFilterValue, f: T): o is T => {
|
||||||
|
const areArrays = Array.isArray(o) && Array.isArray(f);
|
||||||
|
const areObjects = typeof o === 'object' && typeof f === 'object';
|
||||||
|
return areArrays || areObjects;
|
||||||
|
};
|
||||||
|
return typeof o === typeof f || checkIfIsCorrectObjectType(o, f);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!filters || !values) return false;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{Object.entries(filters).map(([key, filter]) => {
|
||||||
|
if (!(key in values)) {
|
||||||
|
console.error(`No filter value for ${key} in filter values!`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
switch (filter.type) {
|
||||||
|
case FilterTypes.Picker: {
|
||||||
|
// Check if filterValues have correct type
|
||||||
|
// this needs to be inside of every case in this switch to get correct value type
|
||||||
|
const value = values[key].value; // here value has every possible value type
|
||||||
|
// We could just do `as typeof filter.value` but just to be sure I made a typeguard
|
||||||
|
if (!isValueCorrectType<typeof filter.value>(value, filter.value)) {
|
||||||
|
console.error(
|
||||||
|
`FilterValue for filter [${key}] has a wrong type!`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// value; // here value has only Picker's value
|
||||||
|
return (
|
||||||
|
<PickerFilter
|
||||||
|
key={`picker_filter_${key}`}
|
||||||
|
filter={{ key, filter }}
|
||||||
|
value={value}
|
||||||
|
set={newValue => set(key, newValue)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case FilterTypes.Switch: {
|
||||||
|
// Check if filterValues have correct type
|
||||||
|
// this needs to be inside of every case in this switch to get correct value type
|
||||||
|
const value = values[key].value; // here value has every possible value type
|
||||||
|
// We could just do `as typeof filter.value` but just to be sure I made a typeguard
|
||||||
|
if (!isValueCorrectType<typeof filter.value>(value, filter.value)) {
|
||||||
|
console.error(
|
||||||
|
`FilterValue for filter [${key}] has a wrong type!`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// value; // here value has only Picker's value
|
||||||
|
return (
|
||||||
|
<SwitchFilter
|
||||||
|
filter={{ key, filter }}
|
||||||
|
key={`switch_filter_${key}`}
|
||||||
|
value={value}
|
||||||
|
set={newValue => set(key, newValue)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case FilterTypes.TextInput: {
|
||||||
|
const value = values[key].value;
|
||||||
|
if (!isValueCorrectType(value, filter.value)) {
|
||||||
|
console.error(
|
||||||
|
`FilterValue for filter [${key}] has a wrong type!`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<TextFilter
|
||||||
|
filter={{ key, filter }}
|
||||||
|
set={newValue => set(key, newValue)}
|
||||||
|
value={value}
|
||||||
|
key={`text_filter_${key}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case FilterTypes.CheckboxGroup: {
|
||||||
|
const value = values[key].value;
|
||||||
|
if (!isValueCorrectType(value, filter.value)) {
|
||||||
|
console.error(
|
||||||
|
`FilterValue for filter [${key}] has a wrong type!`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<CheckboxFilter
|
||||||
|
filter={{ key, filter }}
|
||||||
|
set={newValue => set(key, newValue)}
|
||||||
|
value={value}
|
||||||
|
key={`checkbox_filter_${key}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case FilterTypes.ExcludableCheckboxGroup: {
|
||||||
|
const value = values[key].value;
|
||||||
|
if (!isValueCorrectType(value, filter.value)) {
|
||||||
|
console.error(
|
||||||
|
`FilterValue for filter [${key}] has a wrong type!`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<XCheckboxFilter
|
||||||
|
filter={{ key, filter }}
|
||||||
|
set={newValue => set(key, newValue)}
|
||||||
|
value={value}
|
||||||
|
key={`checkbox_filter_${key}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DialogRef = { show(): void; hide(): void };
|
||||||
|
|
||||||
|
export const FiltersDialog = forwardRef<
|
||||||
|
DialogRef,
|
||||||
|
{
|
||||||
|
values: FilterToValues<Filters> | undefined;
|
||||||
|
filters: Filters | undefined;
|
||||||
|
setValues: React.Dispatch<
|
||||||
|
React.SetStateAction<FilterToValues<Filters> | undefined>
|
||||||
|
>;
|
||||||
|
refetch(): void;
|
||||||
|
}
|
||||||
|
>(function FiltersDialog({ values, setValues, filters, refetch }, ref) {
|
||||||
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
|
const [filterElements, setFilterElements] = useState<ReactNode>(false);
|
||||||
|
const setFilterWithKey = (key: string, newValue: AnyFilterValue) =>
|
||||||
|
setValues(fValues =>
|
||||||
|
!fValues
|
||||||
|
? fValues
|
||||||
|
: {
|
||||||
|
...fValues,
|
||||||
|
[key]: {
|
||||||
|
...fValues[key],
|
||||||
|
value: newValue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
show() {
|
||||||
|
setDialogOpen(true);
|
||||||
|
},
|
||||||
|
hide() {
|
||||||
|
setDialogOpen(false);
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFilterElements(renderFilters(filters, values, setFilterWithKey));
|
||||||
|
}, [values]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
scroll="paper"
|
||||||
|
open={dialogOpen}
|
||||||
|
onClose={() => setDialogOpen(false)}
|
||||||
|
>
|
||||||
|
<DialogTitle>Filters</DialogTitle>
|
||||||
|
<DialogContent dividers id="filtersContent">
|
||||||
|
{filterElements}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => refetch()}>Refetch</Button>
|
||||||
|
<Button style={{ color: 'red' }} onClick={() => setDialogOpen(false)}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Filter, FilterTypes, ValueOfFilter } from '@libs/filterInputs';
|
||||||
|
import { FormControl, InputLabel, MenuItem, Select } from '@mui/material';
|
||||||
|
|
||||||
|
type FilterType = FilterTypes.Picker;
|
||||||
|
|
||||||
|
export const PickerFilter = ({
|
||||||
|
filter: { key, filter },
|
||||||
|
value,
|
||||||
|
set,
|
||||||
|
}: {
|
||||||
|
filter: { key: string; filter: Filter<FilterType> };
|
||||||
|
value: ValueOfFilter<FilterType>;
|
||||||
|
set: (v: ValueOfFilter<FilterType>) => void;
|
||||||
|
}) => {
|
||||||
|
const label = filter.label.replace(/s+/gi, '-');
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
key={key}
|
||||||
|
variant="filled"
|
||||||
|
fullWidth
|
||||||
|
margin="dense"
|
||||||
|
style={{ alignSelf: 'center' }}
|
||||||
|
>
|
||||||
|
<InputLabel id={`picker_filter_label_${label}_${key}`}>
|
||||||
|
{filter.label}
|
||||||
|
</InputLabel>
|
||||||
|
<Select
|
||||||
|
size="medium"
|
||||||
|
labelId={`picker_filter_label_${label}_${key}`}
|
||||||
|
id={`picker_filter_${label}_${key}`}
|
||||||
|
value={value || key}
|
||||||
|
onChange={({ target: { value: newValue } }) => {
|
||||||
|
if (newValue === key) newValue = '';
|
||||||
|
set(newValue);
|
||||||
|
}}
|
||||||
|
MenuProps={{ slotProps: { paper: { sx: { maxHeight: 36 * 4 } } } }}
|
||||||
|
label={filter.label}
|
||||||
|
>
|
||||||
|
{filter.options.map(option => {
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
style={{ minHeight: 36 }}
|
||||||
|
key={`${key}_${option.value}`}
|
||||||
|
value={
|
||||||
|
/** Because Mui treats empty string as `empty value`
|
||||||
|
* and doesn't show the MenuItem, if the value is empty, just use the key...
|
||||||
|
* We could do some other weird stuff, but I doubt there will be a filter
|
||||||
|
* that has the same name as the value being used inside of it.
|
||||||
|
* We can warn people in the documentation about this behavior */
|
||||||
|
option.value || key
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Filter, FilterTypes, ValueOfFilter } from '@libs/filterInputs';
|
||||||
|
import { FormControl, FormControlLabel } from '@mui/material';
|
||||||
|
import { StyledSwitch } from '../../../StyledSwitch';
|
||||||
|
|
||||||
|
type FilterType = FilterTypes.Switch;
|
||||||
|
|
||||||
|
export const SwitchFilter = ({
|
||||||
|
filter: { key, filter },
|
||||||
|
value,
|
||||||
|
set,
|
||||||
|
}: {
|
||||||
|
filter: { key: string; filter: Filter<FilterType> };
|
||||||
|
value: ValueOfFilter<FilterType>;
|
||||||
|
set: (v: typeof value) => void;
|
||||||
|
}) => {
|
||||||
|
const label = filter.label.replace(/s+/gi, '-');
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
key={key}
|
||||||
|
className="switch_filter"
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
>
|
||||||
|
<FormControlLabel
|
||||||
|
style={{ userSelect: 'none' }}
|
||||||
|
label={filter.label}
|
||||||
|
id={`switch_filter_${label}_${key}`}
|
||||||
|
control={<StyledSwitch onChange={() => set(!value)} />}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import React, { useRef } from 'react';
|
||||||
|
import { Filter, FilterTypes, ValueOfFilter } from '@libs/filterInputs';
|
||||||
|
import { FormControl, TextField } from '@mui/material';
|
||||||
|
|
||||||
|
type FilterType = FilterTypes.TextInput;
|
||||||
|
|
||||||
|
export const TextFilter = ({
|
||||||
|
filter: { key, filter },
|
||||||
|
value,
|
||||||
|
set,
|
||||||
|
}: {
|
||||||
|
filter: { key: string; filter: Filter<FilterType> };
|
||||||
|
value: ValueOfFilter<FilterType>;
|
||||||
|
set: (v: typeof value) => void;
|
||||||
|
}) => {
|
||||||
|
const label = filter.label.replace(/s+/gi, '-');
|
||||||
|
const textRef = useRef<HTMLInputElement>(null);
|
||||||
|
return (
|
||||||
|
<FormControl key={key} className="switch_filter">
|
||||||
|
<TextField
|
||||||
|
color="primary"
|
||||||
|
id={`text_filter_${label}_${key}`}
|
||||||
|
inputRef={textRef}
|
||||||
|
label={filter.label}
|
||||||
|
value={value}
|
||||||
|
type="text"
|
||||||
|
variant="filled"
|
||||||
|
fullWidth
|
||||||
|
margin="dense"
|
||||||
|
onInput={() => {
|
||||||
|
if (textRef.current) {
|
||||||
|
set(textRef.current.value ?? '');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
import { styled } from '@mui/system';
|
||||||
|
import React, { useRef, useState } from 'react';
|
||||||
|
|
||||||
|
class TriStateCheckboxClasses {
|
||||||
|
root = 'TriStateCheckboxRoot';
|
||||||
|
base = 'TriStateCheckboxBase';
|
||||||
|
input = 'TriStateCheckboxInput';
|
||||||
|
svg = 'TriStateCheckboxSVG';
|
||||||
|
checked: string;
|
||||||
|
unchecked: string;
|
||||||
|
neutral: string;
|
||||||
|
offsets = {
|
||||||
|
neutral: 16,
|
||||||
|
checked: 16 * 2,
|
||||||
|
unchecked: 0,
|
||||||
|
};
|
||||||
|
constructor() {
|
||||||
|
this.checked = `${this.base}-checked`;
|
||||||
|
this.neutral = `${this.base}-neutral`;
|
||||||
|
this.unchecked = `${this.base}-unchecked`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const classes = new TriStateCheckboxClasses();
|
||||||
|
|
||||||
|
const TriStateCheckboxRoot = styled(`span`, {
|
||||||
|
name: classes.root,
|
||||||
|
slot: 'root',
|
||||||
|
})(({ theme }) => ({
|
||||||
|
[`&.${classes.checked}`]: {
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
},
|
||||||
|
[`&.${classes.unchecked}`]: {
|
||||||
|
color: 'red',
|
||||||
|
},
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
position: 'relative',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
outline: 0,
|
||||||
|
border: 0,
|
||||||
|
margin: 0,
|
||||||
|
cursor: 'pointer',
|
||||||
|
userSelect: 'none',
|
||||||
|
textDecoration: 'none',
|
||||||
|
borderRadius: '50%',
|
||||||
|
padding: '9px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TriStateCheckboxInput = styled(`input`, {
|
||||||
|
name: classes.input,
|
||||||
|
slot: 'input',
|
||||||
|
})(() => ({
|
||||||
|
cursor: 'inherit',
|
||||||
|
position: 'absolute',
|
||||||
|
opacity: 0,
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
margin: 0,
|
||||||
|
padding: 0,
|
||||||
|
zIndex: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TriStateCheckboxSVG = styled(`svg`, { name: classes.svg, slot: 'image' })(
|
||||||
|
() => ({
|
||||||
|
userSelect: 'none',
|
||||||
|
width: '1em',
|
||||||
|
height: '1em',
|
||||||
|
display: 'inline-block',
|
||||||
|
fill: 'currentcolor',
|
||||||
|
flexShrink: 0,
|
||||||
|
fontSize: '1.5rem',
|
||||||
|
transition: 'fill 200ms cubic-bezier(0.4,0,0.2,1) 0ms',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const emptyPath = (
|
||||||
|
<path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"></path>
|
||||||
|
);
|
||||||
|
|
||||||
|
const checkmarkPath = (
|
||||||
|
<path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"></path>
|
||||||
|
);
|
||||||
|
|
||||||
|
const uncheckedPath = (
|
||||||
|
<path
|
||||||
|
d={`M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zM8 6l4 4l4-4l2 2l-4 4l4,4l-2 2l-4-4l-4 4l-2-2l4-4l-4-4z`}
|
||||||
|
></path>
|
||||||
|
);
|
||||||
|
|
||||||
|
export type TriStateCheckboxState = 'checked' | 'unchecked' | 'neutral';
|
||||||
|
|
||||||
|
export const TriStateCheckbox = function ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
value?: TriStateCheckboxState;
|
||||||
|
onChange?: (newValue: TriStateCheckboxState) => void;
|
||||||
|
}) {
|
||||||
|
const [state, sSt] = useState<TriStateCheckboxState>(value ?? 'neutral');
|
||||||
|
const setState = (v: TriStateCheckboxState) => {
|
||||||
|
onChange?.(v);
|
||||||
|
sSt(value ?? v);
|
||||||
|
};
|
||||||
|
const root = useRef<HTMLSpanElement>(null);
|
||||||
|
|
||||||
|
const curVal = value ?? state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TriStateCheckboxRoot
|
||||||
|
className={classes[curVal]}
|
||||||
|
data-state={curVal}
|
||||||
|
ref={root}
|
||||||
|
onClick={() => {
|
||||||
|
switch (curVal) {
|
||||||
|
case 'checked':
|
||||||
|
return setState('unchecked');
|
||||||
|
case 'neutral':
|
||||||
|
return setState('checked');
|
||||||
|
case 'unchecked':
|
||||||
|
return setState('neutral');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TriStateCheckboxInput />
|
||||||
|
<TriStateCheckboxSVG>
|
||||||
|
{curVal === 'checked'
|
||||||
|
? checkmarkPath
|
||||||
|
: curVal === 'unchecked'
|
||||||
|
? uncheckedPath
|
||||||
|
: emptyPath}
|
||||||
|
</TriStateCheckboxSVG>
|
||||||
|
</TriStateCheckboxRoot>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
import { styled } from '@mui/system';
|
||||||
|
import React, { useRef, useState } from 'react';
|
||||||
|
|
||||||
|
class TriStateSwitchClasses {
|
||||||
|
root = 'TriStateSwitchRoot';
|
||||||
|
base = 'TriStateSwitchBase';
|
||||||
|
track = 'TriStateSwitchTrack';
|
||||||
|
thumb = 'TriStateSwitchThumb';
|
||||||
|
input = 'TriStateSwitchInput';
|
||||||
|
checked: string;
|
||||||
|
unchecked: string;
|
||||||
|
neutral: string;
|
||||||
|
offsets = {
|
||||||
|
neutral: 18,
|
||||||
|
checked: 18 * 2,
|
||||||
|
unchecked: 0,
|
||||||
|
};
|
||||||
|
width = 16 * 4 + 10;
|
||||||
|
constructor() {
|
||||||
|
this.base;
|
||||||
|
this.checked = `${this.base}-checked`;
|
||||||
|
this.neutral = `${this.base}-neutral`;
|
||||||
|
this.unchecked = `${this.base}-unchecked`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const classes = new TriStateSwitchClasses();
|
||||||
|
|
||||||
|
const TriStateSwitchRoot = styled(`span`, {
|
||||||
|
name: classes.root,
|
||||||
|
slot: 'root',
|
||||||
|
})(() => ({
|
||||||
|
display: 'inline-flex',
|
||||||
|
width: `${classes.width}px`,
|
||||||
|
height: '38px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
position: 'relative',
|
||||||
|
flexShrink: 0,
|
||||||
|
zIndex: 0,
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
padding: '8px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TriStateSwitchTrack = styled(`span`, {
|
||||||
|
name: classes.track,
|
||||||
|
slot: 'track',
|
||||||
|
})(() => ({
|
||||||
|
// Android12 visuals:
|
||||||
|
borderRadius: 22 / 2,
|
||||||
|
// base Track
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
zIndex: -1,
|
||||||
|
content: `''`,
|
||||||
|
transition:
|
||||||
|
'opacity 150ms cubic-bezier(0.4,0,0.2,1) 0ms, background-color 150ms cubic-bezier(0.4,0,0.2,1) 0ms',
|
||||||
|
backgroundColor: '#000',
|
||||||
|
opacity: 0.38,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TriStateSwitchBase = styled(`span`, {
|
||||||
|
name: classes.base,
|
||||||
|
slot: 'base',
|
||||||
|
})(({ theme }) => ({
|
||||||
|
[`&.${classes.unchecked}`]: {
|
||||||
|
transform: `translateX(${classes.offsets.unchecked}px)`,
|
||||||
|
color: '#f33',
|
||||||
|
},
|
||||||
|
[`&.${classes.checked}`]: {
|
||||||
|
transform: `translateX(${classes.offsets.checked}px)`,
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
},
|
||||||
|
[`&.${classes.neutral}`]: {
|
||||||
|
transform: `translateX(${classes.offsets.neutral}px)`,
|
||||||
|
color: 'gray',
|
||||||
|
},
|
||||||
|
|
||||||
|
[`&.${classes.checked} + span`]: {
|
||||||
|
opacity: 0.5,
|
||||||
|
backgroundColor: theme.palette.primary.main,
|
||||||
|
},
|
||||||
|
[`&.${classes.unchecked} + span`]: {
|
||||||
|
opacity: 0.5,
|
||||||
|
backgroundColor: '#f33',
|
||||||
|
},
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
outline: 0,
|
||||||
|
border: 0,
|
||||||
|
margin: 0,
|
||||||
|
cursor: 'pointer',
|
||||||
|
userSelect: 'none',
|
||||||
|
textDecoration: 'none',
|
||||||
|
padding: '9px',
|
||||||
|
borderRadius: '50%',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
zIndex: 1,
|
||||||
|
color: '#fff',
|
||||||
|
transition:
|
||||||
|
'left 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,transform 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TriStateSwitchInput = styled(`input`, {
|
||||||
|
name: classes.input,
|
||||||
|
slot: 'input',
|
||||||
|
})(() => ({
|
||||||
|
left: '-100%',
|
||||||
|
width: '300%',
|
||||||
|
cursor: 'inherit',
|
||||||
|
position: 'absolute',
|
||||||
|
opacity: 0,
|
||||||
|
height: '100%',
|
||||||
|
top: 0,
|
||||||
|
margin: 0,
|
||||||
|
padding: 0,
|
||||||
|
zIndex: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TriStateSwitchThumb = styled(`span`, {
|
||||||
|
name: classes.thumb,
|
||||||
|
slot: 'thumb',
|
||||||
|
})(() => ({
|
||||||
|
boxShadow: 'none',
|
||||||
|
width: '16px',
|
||||||
|
height: '16px',
|
||||||
|
margin: '2px',
|
||||||
|
backgroundColor: 'currentColor',
|
||||||
|
borderRadius: '50%',
|
||||||
|
}));
|
||||||
|
type TriStateSwitchState = 'checked' | 'unchecked' | 'neutral';
|
||||||
|
|
||||||
|
export const TriStateSwitch = function ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
value?: TriStateSwitchState;
|
||||||
|
onChange?: (newValue: TriStateSwitchState) => void;
|
||||||
|
}) {
|
||||||
|
const [state, sSt] = useState<TriStateSwitchState>(value ?? 'neutral');
|
||||||
|
const setState = (v: TriStateSwitchState) => {
|
||||||
|
onChange?.(v);
|
||||||
|
sSt(value ?? v);
|
||||||
|
};
|
||||||
|
|
||||||
|
const root = useRef<HTMLSpanElement>(null);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TriStateSwitchRoot
|
||||||
|
data-state={state}
|
||||||
|
ref={root}
|
||||||
|
onClick={e => {
|
||||||
|
const offsetX =
|
||||||
|
e.clientX - (root.current?.getBoundingClientRect().x ?? 0);
|
||||||
|
|
||||||
|
const inputWidth = parseFloat(
|
||||||
|
getComputedStyle(e.currentTarget).width,
|
||||||
|
);
|
||||||
|
const clickPercentage = offsetX / inputWidth;
|
||||||
|
const sidePressed =
|
||||||
|
clickPercentage < 0.33
|
||||||
|
? 'left'
|
||||||
|
: clickPercentage > 0.66
|
||||||
|
? 'right'
|
||||||
|
: 'center';
|
||||||
|
|
||||||
|
console.log(inputWidth, offsetX);
|
||||||
|
|
||||||
|
switch (sidePressed) {
|
||||||
|
case 'center':
|
||||||
|
return setState('neutral');
|
||||||
|
case 'left':
|
||||||
|
return setState('unchecked');
|
||||||
|
case 'right':
|
||||||
|
return setState('checked');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TriStateSwitchBase className={classes[value ?? state]}>
|
||||||
|
<TriStateSwitchInput />
|
||||||
|
<TriStateSwitchThumb />
|
||||||
|
</TriStateSwitchBase>
|
||||||
|
<TriStateSwitchTrack />
|
||||||
|
</TriStateSwitchRoot>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Filter, FilterTypes, ValueOfFilter } from '@libs/filterInputs';
|
||||||
|
import { FormControl, FormControlLabel, FormLabel, Grid } from '@mui/material';
|
||||||
|
import { useAppStore } from '@store';
|
||||||
|
import { TriStateCheckbox, TriStateCheckboxState } from './TriStateCheckbox';
|
||||||
|
import { TriStateSwitch } from './TriStateSwitch';
|
||||||
|
|
||||||
|
type FilterType = FilterTypes.ExcludableCheckboxGroup;
|
||||||
|
|
||||||
|
export const XCheckboxFilter = ({
|
||||||
|
filter: { key, filter },
|
||||||
|
value,
|
||||||
|
set,
|
||||||
|
}: {
|
||||||
|
filter: { key: string; filter: Filter<FilterType> };
|
||||||
|
value: ValueOfFilter<FilterType>;
|
||||||
|
set: (v: typeof value) => void;
|
||||||
|
}) => {
|
||||||
|
const useSwitches = useAppStore(state => state.useSwitches);
|
||||||
|
|
||||||
|
const getCheckboxState = (v: string): TriStateCheckboxState => {
|
||||||
|
if (value.include?.includes(v)) return 'checked';
|
||||||
|
if (value.exclude?.includes(v)) return 'unchecked';
|
||||||
|
return 'neutral';
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (key: string, v: TriStateCheckboxState) => {
|
||||||
|
console.log(`setting ${key} to`, v);
|
||||||
|
if (v === 'unchecked')
|
||||||
|
return set({
|
||||||
|
exclude: value.exclude
|
||||||
|
? Array.from(new Set([...value.exclude, key]))
|
||||||
|
: [key],
|
||||||
|
include: value.include?.filter(f => f !== key),
|
||||||
|
});
|
||||||
|
if (v === 'checked')
|
||||||
|
return set({
|
||||||
|
include: value.include
|
||||||
|
? Array.from(new Set([...value.include, key]))
|
||||||
|
: [key],
|
||||||
|
exclude: value.exclude?.filter(f => f !== key),
|
||||||
|
});
|
||||||
|
return set({
|
||||||
|
include: value.include?.filter(f => f !== key),
|
||||||
|
exclude: value.exclude?.filter(f => f !== key),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl key={key} variant="outlined">
|
||||||
|
<FormLabel>{filter.label}</FormLabel>
|
||||||
|
<Grid container columns={{ xs: 3 }}>
|
||||||
|
{filter.options.map(option => {
|
||||||
|
return (
|
||||||
|
<Grid
|
||||||
|
key={`${key}_${option.label}`}
|
||||||
|
item
|
||||||
|
xs={1}
|
||||||
|
justifyContent={'center'}
|
||||||
|
display={'flex'}
|
||||||
|
>
|
||||||
|
<FormControl>
|
||||||
|
<FormControlLabel
|
||||||
|
labelPlacement={useSwitches ? 'top' : 'end'}
|
||||||
|
style={{ userSelect: 'none' }}
|
||||||
|
control={
|
||||||
|
useSwitches ? (
|
||||||
|
<TriStateSwitch
|
||||||
|
value={getCheckboxState(option.value)}
|
||||||
|
onChange={c => handleChange(option.value, c)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<TriStateCheckbox
|
||||||
|
value={getCheckboxState(option.value)}
|
||||||
|
onChange={c => handleChange(option.value, c)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
label={option.label}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import { AppState } from '@redux/store';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { Plugin } from '@typings/plugin';
|
|
||||||
import { getPlugin } from '@provider/plugins';
|
|
||||||
|
|
||||||
export default function usePlugin() {
|
|
||||||
const selectedPluginItem = useSelector(
|
|
||||||
(state: AppState) => state.plugin.selected,
|
|
||||||
);
|
|
||||||
const [plugin, setPlugin] = useState<Plugin.PluginBase | undefined>();
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedPluginItem) {
|
|
||||||
setPlugin(getPlugin(selectedPluginItem.id));
|
|
||||||
}
|
|
||||||
}, [selectedPluginItem]);
|
|
||||||
return plugin;
|
|
||||||
}
|
|
||||||
+4
-4
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { parse as parseProto } from 'protobufjs';
|
import { parse as parseProto } from 'protobufjs';
|
||||||
|
|
||||||
interface FetchInit {
|
type FetchInit = {
|
||||||
headers?: Record<string, string | undefined> | Headers;
|
headers?: Record<string, string | undefined> | Headers;
|
||||||
method?: string;
|
method?: string;
|
||||||
body?: FormData | string;
|
body?: FormData | string;
|
||||||
@@ -12,7 +12,7 @@ interface FetchInit {
|
|||||||
| undefined
|
| undefined
|
||||||
| FormData
|
| FormData
|
||||||
| Headers;
|
| Headers;
|
||||||
}
|
};
|
||||||
|
|
||||||
const makeInit = async (init?: FetchInit) => {
|
const makeInit = async (init?: FetchInit) => {
|
||||||
const defaultHeaders: Record<string, string> = {
|
const defaultHeaders: Record<string, string> = {
|
||||||
@@ -100,13 +100,13 @@ export const fetchText = async function (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ProtoRequestInit {
|
type ProtoRequestInit = {
|
||||||
// merged .proto file
|
// merged .proto file
|
||||||
proto: string;
|
proto: string;
|
||||||
requestType: string;
|
requestType: string;
|
||||||
requestData?: any;
|
requestData?: any;
|
||||||
responseType: string;
|
responseType: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
const BYTE_MARK = BigInt((1 << 8) - 1);
|
const BYTE_MARK = BigInt((1 << 8) - 1);
|
||||||
|
|
||||||
|
|||||||
+80
-28
@@ -1,8 +1,14 @@
|
|||||||
export interface FilterOption {
|
/**
|
||||||
|
* Filter's Options user can choose from
|
||||||
|
*/
|
||||||
|
export type FilterOption = {
|
||||||
readonly label: string;
|
readonly label: string;
|
||||||
readonly value: string;
|
readonly value: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every currently implemented FilterType
|
||||||
|
*/
|
||||||
export enum FilterTypes {
|
export enum FilterTypes {
|
||||||
TextInput = 'Text',
|
TextInput = 'Text',
|
||||||
Picker = 'Picker',
|
Picker = 'Picker',
|
||||||
@@ -11,59 +17,83 @@ export enum FilterTypes {
|
|||||||
ExcludableCheckboxGroup = 'XCheckbox',
|
ExcludableCheckboxGroup = 'XCheckbox',
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SwitchFilter {
|
type SwitchFilter = {
|
||||||
type: FilterTypes.Switch;
|
type: FilterTypes.Switch;
|
||||||
/** Default value */
|
/** Default value */
|
||||||
value: boolean;
|
value: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface TextFilter {
|
type TextFilter = {
|
||||||
type: FilterTypes.TextInput;
|
type: FilterTypes.TextInput;
|
||||||
/** Default value */
|
/** Default value */
|
||||||
value: string;
|
value: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface CheckboxFilter {
|
type CheckboxFilter = {
|
||||||
type: FilterTypes.CheckboxGroup;
|
type: FilterTypes.CheckboxGroup;
|
||||||
options: readonly FilterOption[];
|
options: readonly FilterOption[];
|
||||||
/** Default value */
|
/** Default value */
|
||||||
value: string[];
|
value: string[];
|
||||||
}
|
};
|
||||||
interface PickerFilter {
|
type PickerFilter = {
|
||||||
type: FilterTypes.Picker;
|
type: FilterTypes.Picker;
|
||||||
options: readonly FilterOption[];
|
options: readonly FilterOption[];
|
||||||
/** Default value */
|
/** Default value */
|
||||||
value: string;
|
value: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ExcludableCheckboxFilter {
|
type ExcludableCheckboxFilter = {
|
||||||
type: FilterTypes.ExcludableCheckboxGroup;
|
type: FilterTypes.ExcludableCheckboxGroup;
|
||||||
options: readonly FilterOption[];
|
options: readonly FilterOption[];
|
||||||
value: { include?: string[]; exclude?: string[] };
|
/** Default value */
|
||||||
}
|
value: {
|
||||||
|
/** Checkboxes marked as included */
|
||||||
|
include?: string[];
|
||||||
|
/** Checkboxes marked as excluded */
|
||||||
|
exclude?: string[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export type Filters = Record<
|
/**
|
||||||
string,
|
* key - filter pairs
|
||||||
{ label: string } & (
|
*/
|
||||||
| PickerFilter
|
export type Filters = Record<string, Filter<FilterTypes>>;
|
||||||
| CheckboxFilter
|
|
||||||
| TextFilter
|
|
||||||
| SwitchFilter
|
|
||||||
| ExcludableCheckboxFilter
|
|
||||||
)
|
|
||||||
>;
|
|
||||||
|
|
||||||
|
/** Mapping of each FilterType to a Filter */
|
||||||
|
type FilterFromType = {
|
||||||
|
[FilterTypes.CheckboxGroup]: CheckboxFilter;
|
||||||
|
[FilterTypes.ExcludableCheckboxGroup]: ExcludableCheckboxFilter;
|
||||||
|
[FilterTypes.Picker]: PickerFilter;
|
||||||
|
[FilterTypes.Switch]: SwitchFilter;
|
||||||
|
[FilterTypes.TextInput]: TextFilter;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get type of a single filter type from the {@link FilterType}
|
||||||
|
*/
|
||||||
|
export type Filter<Type extends FilterTypes> = {
|
||||||
|
label: string;
|
||||||
|
} & FilterFromType[Type];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip {@link FilterObject} object from 'label' and 'options' to get key - filter_value pairs
|
||||||
|
* @see {@link ValueOfFilter}
|
||||||
|
*/
|
||||||
export type FilterToValues<
|
export type FilterToValues<
|
||||||
T extends Record<string, { type: FilterTypes }> | undefined,
|
FilterObject extends Record<string, { type: FilterTypes }> | undefined,
|
||||||
> = T extends undefined
|
> = FilterObject extends undefined
|
||||||
? undefined
|
? undefined
|
||||||
: {
|
: {
|
||||||
[K in keyof T]: Omit<
|
// copy the Filters object, but just get {value,type} pairs instead of the whole Filter object
|
||||||
{ type: NonNullable<T>[K]['type'] } & Filters[string],
|
[SingleFilter in keyof FilterObject]: FilterValueWithType<
|
||||||
'label' | 'options'
|
FilterType<NonNullable<FilterObject>[SingleFilter]>
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get value type for a {@link Filter} given it's FilterType
|
||||||
|
* @see {@link FilterTypes}
|
||||||
|
*/
|
||||||
export type ValueOfFilter<T extends FilterTypes> =
|
export type ValueOfFilter<T extends FilterTypes> =
|
||||||
T extends FilterTypes.CheckboxGroup
|
T extends FilterTypes.CheckboxGroup
|
||||||
? CheckboxFilter['value']
|
? CheckboxFilter['value']
|
||||||
@@ -76,3 +106,25 @@ export type ValueOfFilter<T extends FilterTypes> =
|
|||||||
: T extends FilterTypes.ExcludableCheckboxGroup
|
: T extends FilterTypes.ExcludableCheckboxGroup
|
||||||
? ExcludableCheckboxFilter['value']
|
? ExcludableCheckboxFilter['value']
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
|
/** Get {@link Filter}'s type */
|
||||||
|
export type FilterType<T extends { type: unknown }> = T extends {
|
||||||
|
type: infer K;
|
||||||
|
}
|
||||||
|
? K extends FilterTypes
|
||||||
|
? K
|
||||||
|
: never
|
||||||
|
: never;
|
||||||
|
|
||||||
|
/** Get {type, value} types for given FilterType
|
||||||
|
* @see {@link ValueOfFilter}
|
||||||
|
*/
|
||||||
|
export type FilterValueWithType<T extends FilterTypes> = {
|
||||||
|
type: T;
|
||||||
|
value: ValueOfFilter<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any possible filter value
|
||||||
|
*/
|
||||||
|
export type AnyFilterValue = ValueOfFilter<FilterTypes>;
|
||||||
|
|||||||
@@ -367,14 +367,14 @@ class LnMTLPlugin implements Plugin.PagePlugin {
|
|||||||
|
|
||||||
export default new LnMTLPlugin();
|
export default new LnMTLPlugin();
|
||||||
|
|
||||||
interface ChapterEntry {
|
type ChapterEntry = {
|
||||||
number: number;
|
number: number;
|
||||||
title: string;
|
title: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface VolumeEntry {
|
type VolumeEntry = {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -369,9 +369,9 @@ class RanobesPlugin implements Plugin.PagePlugin {
|
|||||||
}
|
}
|
||||||
export default new RanobesPlugin();
|
export default new RanobesPlugin();
|
||||||
|
|
||||||
interface ChapterEntry {
|
type ChapterEntry = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
date: string;
|
date: string;
|
||||||
link: string;
|
link: string;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import { defaultCover } from '@libs/defaultCover';
|
|||||||
import { NovelItem } from '../../test_web/static/js';
|
import { NovelItem } from '../../test_web/static/js';
|
||||||
// import { isUrlAbsolute } from "@libs/isAbsoluteUrl";
|
// import { isUrlAbsolute } from "@libs/isAbsoluteUrl";
|
||||||
|
|
||||||
interface FuzzySearchOptions {
|
type FuzzySearchOptions = {
|
||||||
caseSensitive: boolean;
|
caseSensitive: boolean;
|
||||||
sort: boolean;
|
sort: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
// Shamelessly stolen from 'https://raw.githubusercontent.com/wouterrutgers/fuzzy-search/master/src/FuzzySearch.mjs'
|
// Shamelessly stolen from 'https://raw.githubusercontent.com/wouterrutgers/fuzzy-search/master/src/FuzzySearch.mjs'
|
||||||
// I did modify the code a fair bit, but the algorithm is still the same under the hood.
|
// I did modify the code a fair bit, but the algorithm is still the same under the hood.
|
||||||
|
|||||||
@@ -722,18 +722,18 @@ class RoyalRoad implements Plugin.PluginBase {
|
|||||||
|
|
||||||
export default new RoyalRoad();
|
export default new RoyalRoad();
|
||||||
|
|
||||||
interface ChapterEntry {
|
type ChapterEntry = {
|
||||||
id: number;
|
id: number;
|
||||||
volumeId: number;
|
volumeId: number;
|
||||||
title: string;
|
title: string;
|
||||||
date: string;
|
date: string;
|
||||||
order: number;
|
order: number;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface VolumeEntry {
|
type VolumeEntry = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
cover: string;
|
cover: string;
|
||||||
order: number;
|
order: number;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -205,22 +205,22 @@ class WTRLAB implements Plugin.PluginBase {
|
|||||||
} satisfies Filters;
|
} satisfies Filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NovelJson {
|
type NovelJson = {
|
||||||
props: Props;
|
props: Props;
|
||||||
page: string;
|
page: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Props {
|
type Props = {
|
||||||
pageProps: PageProps;
|
pageProps: PageProps;
|
||||||
__N_SSP: boolean;
|
__N_SSP: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface PageProps {
|
type PageProps = {
|
||||||
serie: Serie;
|
serie: Serie;
|
||||||
server_time: Date;
|
server_time: Date;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Serie {
|
type Serie = {
|
||||||
serie_data: SerieData;
|
serie_data: SerieData;
|
||||||
chapters: Chapter[];
|
chapters: Chapter[];
|
||||||
recommendation: SerieData[];
|
recommendation: SerieData[];
|
||||||
@@ -231,9 +231,9 @@ interface Serie {
|
|||||||
data: Data;
|
data: Data;
|
||||||
is_default: boolean;
|
is_default: boolean;
|
||||||
raw_type: string;
|
raw_type: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Chapter {
|
type Chapter = {
|
||||||
serie_id: number;
|
serie_id: number;
|
||||||
id: number;
|
id: number;
|
||||||
order: number;
|
order: number;
|
||||||
@@ -242,16 +242,16 @@ interface Chapter {
|
|||||||
name: string;
|
name: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}
|
};
|
||||||
interface ChapterData {
|
type ChapterData = {
|
||||||
data: ChapterContent;
|
data: ChapterContent;
|
||||||
}
|
};
|
||||||
interface ChapterContent {
|
type ChapterContent = {
|
||||||
title: string;
|
title: string;
|
||||||
body: string;
|
body: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface SerieData {
|
type SerieData = {
|
||||||
serie_id?: number;
|
serie_id?: number;
|
||||||
recommendation_id?: number;
|
recommendation_id?: number;
|
||||||
score?: string;
|
score?: string;
|
||||||
@@ -273,26 +273,26 @@ interface SerieData {
|
|||||||
from: null;
|
from: null;
|
||||||
raw_id: number;
|
raw_id: number;
|
||||||
genres?: number[];
|
genres?: number[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Data {
|
type Data = {
|
||||||
title: string;
|
title: string;
|
||||||
author: string;
|
author: string;
|
||||||
description: string;
|
description: string;
|
||||||
image: string;
|
image: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface JsonNovel {
|
type JsonNovel = {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
data: Datum[];
|
data: Datum[];
|
||||||
}
|
};
|
||||||
interface Datum {
|
type Datum = {
|
||||||
serie: Serie;
|
serie: Serie;
|
||||||
chapters: Chapter[];
|
chapters: Chapter[];
|
||||||
updated_at: Date;
|
updated_at: Date;
|
||||||
raw_id: number;
|
raw_id: number;
|
||||||
slug: string;
|
slug: string;
|
||||||
data: Data;
|
data: Data;
|
||||||
}
|
};
|
||||||
|
|
||||||
export default new WTRLAB();
|
export default new WTRLAB();
|
||||||
|
|||||||
@@ -4,35 +4,35 @@ import { Plugin } from '@typings/plugin';
|
|||||||
import { Filters } from '@libs/filterInputs';
|
import { Filters } from '@libs/filterInputs';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
|
|
||||||
interface StringValue {
|
type StringValue = {
|
||||||
value: string;
|
value: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface BoolValue {
|
type BoolValue = {
|
||||||
value: boolean;
|
value: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface DecimalValue {
|
type DecimalValue = {
|
||||||
units: bigint;
|
units: bigint;
|
||||||
nanos: number;
|
nanos: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Timestamp {
|
type Timestamp = {
|
||||||
seconds: number;
|
seconds: number;
|
||||||
nanos: number;
|
nanos: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface NovelEntry {
|
type NovelEntry = {
|
||||||
name: string;
|
name: string;
|
||||||
coverUrl: string;
|
coverUrl: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface RelatedChapterUserInfo {
|
type RelatedChapterUserInfo = {
|
||||||
isChapterUnlocked?: BoolValue;
|
isChapterUnlocked?: BoolValue;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ChapterItem {
|
type ChapterItem = {
|
||||||
entityId: number;
|
entityId: number;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
@@ -41,19 +41,19 @@ interface ChapterItem {
|
|||||||
relatedUserInfo?: RelatedChapterUserInfo;
|
relatedUserInfo?: RelatedChapterUserInfo;
|
||||||
offset: number;
|
offset: number;
|
||||||
publishedAt?: Timestamp;
|
publishedAt?: Timestamp;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface GetChapterResponse {
|
type GetChapterResponse = {
|
||||||
item?: ChapterItem;
|
item?: ChapterItem;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ChapterGroupCounts {
|
type ChapterGroupCounts = {
|
||||||
total: number;
|
total: number;
|
||||||
advance: number;
|
advance: number;
|
||||||
normal: number;
|
normal: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ChapterGroupItem {
|
type ChapterGroupItem = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
order: number;
|
order: number;
|
||||||
@@ -61,11 +61,11 @@ interface ChapterGroupItem {
|
|||||||
toChapterNumber?: DecimalValue;
|
toChapterNumber?: DecimalValue;
|
||||||
chapterList: ChapterItem[];
|
chapterList: ChapterItem[];
|
||||||
counts?: ChapterGroupCounts;
|
counts?: ChapterGroupCounts;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface GetChapterListResponse {
|
type GetChapterListResponse = {
|
||||||
items: ChapterGroupItem[];
|
items: ChapterGroupItem[];
|
||||||
}
|
};
|
||||||
|
|
||||||
enum NovelItem_Status {
|
enum NovelItem_Status {
|
||||||
Finished = 0,
|
Finished = 0,
|
||||||
@@ -74,14 +74,14 @@ enum NovelItem_Status {
|
|||||||
All = -1,
|
All = -1,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NovelKarmaInfo {
|
type NovelKarmaInfo = {
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
isFree: boolean;
|
isFree: boolean;
|
||||||
maxFreeChapter?: DecimalValue;
|
maxFreeChapter?: DecimalValue;
|
||||||
canUnlockWithVip: boolean;
|
canUnlockWithVip: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface NovelItem {
|
type NovelItem = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
@@ -94,11 +94,11 @@ interface NovelItem {
|
|||||||
authorName?: StringValue;
|
authorName?: StringValue;
|
||||||
karmaInfo?: NovelKarmaInfo;
|
karmaInfo?: NovelKarmaInfo;
|
||||||
genres: string[];
|
genres: string[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface GetNovelResponse {
|
type GetNovelResponse = {
|
||||||
item?: NovelItem;
|
item?: NovelItem;
|
||||||
}
|
};
|
||||||
|
|
||||||
class WuxiaWorld implements Plugin.PluginBase {
|
class WuxiaWorld implements Plugin.PluginBase {
|
||||||
id = 'wuxiaworld';
|
id = 'wuxiaworld';
|
||||||
|
|||||||
Vendored
+3
-2
@@ -1,3 +1,4 @@
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '../types/plugin';
|
||||||
|
|
||||||
export default [] as Plugin.PluginBase[];
|
declare const PLUGINS: Plugin.PluginItem[];
|
||||||
|
export default PLUGINS;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { fetchApi } from '@libs/fetch';
|
|||||||
import { Plugin } from '@typings/plugin';
|
import { Plugin } from '@typings/plugin';
|
||||||
import { defaultCover } from '@libs/defaultCover';
|
import { defaultCover } from '@libs/defaultCover';
|
||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { NovelItem } from '../../test_web/static/js';
|
|
||||||
// const novelStatus = require('@libs/novelStatus');
|
// const novelStatus = require('@libs/novelStatus');
|
||||||
// const isUrlAbsolute = require('@libs/isAbsoluteUrl');
|
// const isUrlAbsolute = require('@libs/isAbsoluteUrl');
|
||||||
// const parseDate = require('@libs/parseDate');
|
// const parseDate = require('@libs/parseDate');
|
||||||
@@ -52,13 +51,13 @@ class Syosetu implements Plugin.PluginBase {
|
|||||||
if (parseInt(loadedCheerio('.is-current').html() || '1') !== pagenumber)
|
if (parseInt(loadedCheerio('.is-current').html() || '1') !== pagenumber)
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
const novels: NovelItem[] = [];
|
const novels: Plugin.NovelItem[] = [];
|
||||||
loadedCheerio('.c-card').each((_, e) => {
|
loadedCheerio('.c-card').each((_, e) => {
|
||||||
const anchor = loadedCheerio(e).find('.p-ranklist-item__title a');
|
const anchor = loadedCheerio(e).find('.p-ranklist-item__title a');
|
||||||
const url = anchor.attr('href');
|
const url = anchor.attr('href');
|
||||||
if (!url) return;
|
if (!url) return;
|
||||||
const name = anchor.text();
|
const name = anchor.text();
|
||||||
const novel: NovelItem = {
|
const novel: Plugin.NovelItem = {
|
||||||
path: url.replace(this.novelPrefix, ''),
|
path: url.replace(this.novelPrefix, ''),
|
||||||
name,
|
name,
|
||||||
cover: defaultCover,
|
cover: defaultCover,
|
||||||
@@ -93,12 +92,12 @@ class Syosetu implements Plugin.PluginBase {
|
|||||||
novel.summary = loadedCheerio('#novel_ex')
|
novel.summary = loadedCheerio('#novel_ex')
|
||||||
.text()
|
.text()
|
||||||
.replace(/<\s*br.*?>/g, '\n');
|
.replace(/<\s*br.*?>/g, '\n');
|
||||||
cqGetChapters.each(() => {
|
cqGetChapters.each((_, e) => {
|
||||||
const chapterA = loadedCheerio(this).find('a');
|
const chapterA = loadedCheerio(e).find('a');
|
||||||
const [chapterName, releaseDate, chapterUrl] = [
|
const [chapterName, releaseDate, chapterUrl] = [
|
||||||
// set the variables
|
// set the variables
|
||||||
chapterA.text(),
|
chapterA.text(),
|
||||||
loadedCheerio(this)
|
loadedCheerio(e)
|
||||||
.find('dt') // get title
|
.find('dt') // get title
|
||||||
.text() // get text
|
.text() // get text
|
||||||
.replace(/(.)/g, '') // remove "(edited)" mark
|
.replace(/(.)/g, '') // remove "(edited)" mark
|
||||||
|
|||||||
@@ -181,12 +181,12 @@ class Agitoon implements Plugin.PluginBase {
|
|||||||
|
|
||||||
export default new Agitoon();
|
export default new Agitoon();
|
||||||
|
|
||||||
interface response {
|
type response = {
|
||||||
list_limit: number;
|
list_limit: number;
|
||||||
list?: ListEntity[] | null;
|
list?: ListEntity[] | null;
|
||||||
list_count: number;
|
list_count: number;
|
||||||
}
|
};
|
||||||
interface ListEntity {
|
type ListEntity = {
|
||||||
wr_id: string;
|
wr_id: string;
|
||||||
wr_subject: string;
|
wr_subject: string;
|
||||||
np_dir: string;
|
np_dir: string;
|
||||||
@@ -200,13 +200,13 @@ interface ListEntity {
|
|||||||
np_country: string;
|
np_country: string;
|
||||||
np_age: string;
|
np_age: string;
|
||||||
is_scrap: number;
|
is_scrap: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseBook {
|
type responseBook = {
|
||||||
list?: ListEntity2[] | null;
|
list?: ListEntity2[] | null;
|
||||||
download_time: string;
|
download_time: string;
|
||||||
}
|
};
|
||||||
interface ListEntity2 {
|
type ListEntity2 = {
|
||||||
wr_id: string;
|
wr_id: string;
|
||||||
wr_subject: string;
|
wr_subject: string;
|
||||||
wr_datetime: string;
|
wr_datetime: string;
|
||||||
@@ -214,4 +214,4 @@ interface ListEntity2 {
|
|||||||
novel_c_style1: string;
|
novel_c_style1: string;
|
||||||
novel_c_str1: string;
|
novel_c_str1: string;
|
||||||
data_novel_c_view: string;
|
data_novel_c_view: string;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -368,14 +368,14 @@ function decrypt(
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface authorization {
|
type authorization = {
|
||||||
userId: number;
|
userId: number;
|
||||||
token: string;
|
token: string;
|
||||||
issued: string;
|
issued: string;
|
||||||
expires: string;
|
expires: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface currentUser {
|
type currentUser = {
|
||||||
id: number;
|
id: number;
|
||||||
userName: string;
|
userName: string;
|
||||||
fio: string;
|
fio: string;
|
||||||
@@ -391,9 +391,9 @@ interface currentUser {
|
|||||||
emailConfirmed: boolean;
|
emailConfirmed: boolean;
|
||||||
isDeleted: boolean;
|
isDeleted: boolean;
|
||||||
isDisabled: boolean;
|
isDisabled: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface response {
|
type response = {
|
||||||
searchResults?: SearchResultsEntity[] | null;
|
searchResults?: SearchResultsEntity[] | null;
|
||||||
realTotalCount: number;
|
realTotalCount: number;
|
||||||
code?: string;
|
code?: string;
|
||||||
@@ -402,8 +402,8 @@ interface response {
|
|||||||
format: Format;
|
format: Format;
|
||||||
duration: Duration;
|
duration: Duration;
|
||||||
isWorkMarkEnabled: boolean;
|
isWorkMarkEnabled: boolean;
|
||||||
}
|
};
|
||||||
interface SearchResultsEntity {
|
type SearchResultsEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
annotation: string;
|
annotation: string;
|
||||||
@@ -446,20 +446,20 @@ interface SearchResultsEntity {
|
|||||||
isLiked: boolean;
|
isLiked: boolean;
|
||||||
isWorkMarkEnabled: boolean;
|
isWorkMarkEnabled: boolean;
|
||||||
workInLibraryState: string;
|
workInLibraryState: string;
|
||||||
}
|
};
|
||||||
interface Format {
|
type Format = {
|
||||||
enumValue: string;
|
enumValue: string;
|
||||||
value: string;
|
value: string;
|
||||||
title: string;
|
title: string;
|
||||||
mobileTitle: string;
|
mobileTitle: string;
|
||||||
}
|
};
|
||||||
interface Duration {
|
type Duration = {
|
||||||
value: string;
|
value: string;
|
||||||
title: string;
|
title: string;
|
||||||
mobileTitle: string;
|
mobileTitle: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseBook {
|
type responseBook = {
|
||||||
message?: string;
|
message?: string;
|
||||||
chapters?: ChaptersEntity[] | null;
|
chapters?: ChaptersEntity[] | null;
|
||||||
allowDownloads: boolean;
|
allowDownloads: boolean;
|
||||||
@@ -543,8 +543,8 @@ interface responseBook {
|
|||||||
purchaseTime?: null;
|
purchaseTime?: null;
|
||||||
likeTime?: null;
|
likeTime?: null;
|
||||||
markTime?: null;
|
markTime?: null;
|
||||||
}
|
};
|
||||||
interface ChaptersEntity {
|
type ChaptersEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
workId: number;
|
workId: number;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -554,16 +554,16 @@ interface ChaptersEntity {
|
|||||||
lastModificationTime: string;
|
lastModificationTime: string;
|
||||||
textLength: number;
|
textLength: number;
|
||||||
isAvailable: boolean;
|
isAvailable: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface GalleryImagesEntity {
|
type GalleryImagesEntity = {
|
||||||
id: string;
|
id: string;
|
||||||
caption?: null;
|
caption?: null;
|
||||||
url: string;
|
url: string;
|
||||||
height: number;
|
height: number;
|
||||||
width: number;
|
width: number;
|
||||||
}
|
};
|
||||||
interface RecommendationsEntity {
|
type RecommendationsEntity = {
|
||||||
title: string;
|
title: string;
|
||||||
coverUrl: string;
|
coverUrl: string;
|
||||||
authorId: number;
|
authorId: number;
|
||||||
@@ -590,9 +590,9 @@ interface RecommendationsEntity {
|
|||||||
removalReason?: null;
|
removalReason?: null;
|
||||||
removalReasonComment?: null;
|
removalReasonComment?: null;
|
||||||
id: number;
|
id: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface encryptedСhapter {
|
type encryptedСhapter = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
isDraft: boolean;
|
isDraft: boolean;
|
||||||
@@ -602,4 +602,4 @@ interface encryptedСhapter {
|
|||||||
key: string;
|
key: string;
|
||||||
code?: string;
|
code?: string;
|
||||||
message?: string;
|
message?: string;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ class Bookriver implements Plugin.PluginBase {
|
|||||||
}
|
}
|
||||||
export default new Bookriver();
|
export default new Bookriver();
|
||||||
|
|
||||||
interface response {
|
type response = {
|
||||||
props: {
|
props: {
|
||||||
pageProps: {
|
pageProps: {
|
||||||
state: {
|
state: {
|
||||||
@@ -241,9 +241,9 @@ interface response {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
interface BooksEntity {
|
type BooksEntity = {
|
||||||
name: string;
|
name: string;
|
||||||
coverImages: { url: string }[];
|
coverImages: { url: string }[];
|
||||||
slug: string;
|
slug: string;
|
||||||
@@ -256,17 +256,17 @@ interface BooksEntity {
|
|||||||
chapters: Chapter[];
|
chapters: Chapter[];
|
||||||
};
|
};
|
||||||
statusComplete: string;
|
statusComplete: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Chapter {
|
type Chapter = {
|
||||||
name: string;
|
name: string;
|
||||||
available: boolean;
|
available: boolean;
|
||||||
firstPublishedAt?: Date | null;
|
firstPublishedAt?: Date | null;
|
||||||
createdAt?: Date | null;
|
createdAt?: Date | null;
|
||||||
chapterId: number | string;
|
chapterId: number | string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseChapter {
|
type responseChapter = {
|
||||||
data: {
|
data: {
|
||||||
id: number;
|
id: number;
|
||||||
bookId: number;
|
bookId: number;
|
||||||
@@ -288,12 +288,12 @@ interface responseChapter {
|
|||||||
};
|
};
|
||||||
publicationScheduledFor?: null;
|
publicationScheduledFor?: null;
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseSearch {
|
type responseSearch = {
|
||||||
data: Data;
|
data: Data;
|
||||||
total: number;
|
total: number;
|
||||||
}
|
};
|
||||||
interface Data {
|
type Data = {
|
||||||
books?: BooksEntity[] | null;
|
books?: BooksEntity[] | null;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -2650,11 +2650,11 @@ class ficbook implements Plugin.PluginBase {
|
|||||||
|
|
||||||
export default new ficbook();
|
export default new ficbook();
|
||||||
|
|
||||||
interface Data {
|
type Data = {
|
||||||
data?: DataEntity[] | null;
|
data?: DataEntity[] | null;
|
||||||
more: boolean;
|
more: boolean;
|
||||||
}
|
};
|
||||||
interface DataEntity {
|
type DataEntity = {
|
||||||
id: string;
|
id: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
author_id: number;
|
author_id: number;
|
||||||
@@ -2673,4 +2673,4 @@ interface DataEntity {
|
|||||||
readedDate?: string | null;
|
readedDate?: string | null;
|
||||||
reward_cnt: number;
|
reward_cnt: number;
|
||||||
cover?: string | null;
|
cover?: string | null;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -213,13 +213,13 @@ class Neobook implements Plugin.PluginBase {
|
|||||||
|
|
||||||
export default new Neobook();
|
export default new Neobook();
|
||||||
|
|
||||||
interface BundleBooks {
|
type BundleBooks = {
|
||||||
categories: any[];
|
categories: any[];
|
||||||
topSection: any[];
|
topSection: any[];
|
||||||
feed: Novels[];
|
feed: Novels[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface BottomSection {
|
type BottomSection = {
|
||||||
id: string;
|
id: string;
|
||||||
boost: string;
|
boost: string;
|
||||||
preferenceid: string;
|
preferenceid: string;
|
||||||
@@ -230,9 +230,9 @@ interface BottomSection {
|
|||||||
more: string;
|
more: string;
|
||||||
part: string;
|
part: string;
|
||||||
items: Novels[];
|
items: Novels[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Novels {
|
type Novels = {
|
||||||
status?: string;
|
status?: string;
|
||||||
error?: string;
|
error?: string;
|
||||||
login?: Login;
|
login?: Login;
|
||||||
@@ -290,9 +290,9 @@ interface Novels {
|
|||||||
carouselItems?: Novels[];
|
carouselItems?: Novels[];
|
||||||
book?: Novels;
|
book?: Novels;
|
||||||
activeChapterIndex?: number;
|
activeChapterIndex?: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Attachment {
|
type Attachment = {
|
||||||
has: string;
|
has: string;
|
||||||
id: string;
|
id: string;
|
||||||
typeid: string;
|
typeid: string;
|
||||||
@@ -304,16 +304,16 @@ interface Attachment {
|
|||||||
video: string;
|
video: string;
|
||||||
origin: string;
|
origin: string;
|
||||||
ext: string;
|
ext: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Image {
|
type Image = {
|
||||||
s: string;
|
s: string;
|
||||||
m: string;
|
m: string;
|
||||||
l: string;
|
l: string;
|
||||||
has?: string;
|
has?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Chapter {
|
type Chapter = {
|
||||||
id: string;
|
id: string;
|
||||||
token: string;
|
token: string;
|
||||||
status: string;
|
status: string;
|
||||||
@@ -324,9 +324,9 @@ interface Chapter {
|
|||||||
access: string;
|
access: string;
|
||||||
loaded: string;
|
loaded: string;
|
||||||
data?: Data;
|
data?: Data;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Data {
|
type Data = {
|
||||||
available: string;
|
available: string;
|
||||||
access: string;
|
access: string;
|
||||||
id: string;
|
id: string;
|
||||||
@@ -339,27 +339,27 @@ interface Data {
|
|||||||
datePublish: string;
|
datePublish: string;
|
||||||
attachment: Attachment;
|
attachment: Attachment;
|
||||||
lastComments: any[];
|
lastComments: any[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface NovelsCounters {
|
type NovelsCounters = {
|
||||||
impressions: Bookmarks;
|
impressions: Bookmarks;
|
||||||
views: Bookmarks;
|
views: Bookmarks;
|
||||||
likes: Bookmarks;
|
likes: Bookmarks;
|
||||||
comments: Bookmarks;
|
comments: Bookmarks;
|
||||||
bookmarks: Bookmarks;
|
bookmarks: Bookmarks;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Bookmarks {
|
type Bookmarks = {
|
||||||
value: string;
|
value: string;
|
||||||
formatted: string;
|
formatted: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Feed {
|
type Feed = {
|
||||||
name: string;
|
name: string;
|
||||||
canRemoved: string;
|
canRemoved: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Login {
|
type Login = {
|
||||||
uid: string;
|
uid: string;
|
||||||
utoken: string;
|
utoken: string;
|
||||||
has: string;
|
has: string;
|
||||||
@@ -376,14 +376,14 @@ interface Login {
|
|||||||
deposit: any[];
|
deposit: any[];
|
||||||
affiliate: any[];
|
affiliate: any[];
|
||||||
preferences: any[];
|
preferences: any[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface LoginCounters {
|
type LoginCounters = {
|
||||||
notifications: string;
|
notifications: string;
|
||||||
messages: string;
|
messages: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface LoginUser {
|
type LoginUser = {
|
||||||
id: string;
|
id: string;
|
||||||
username: string;
|
username: string;
|
||||||
firstname: string;
|
firstname: string;
|
||||||
@@ -392,39 +392,39 @@ interface LoginUser {
|
|||||||
status: string;
|
status: string;
|
||||||
balance: Balance;
|
balance: Balance;
|
||||||
connectedService: string;
|
connectedService: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Balance {
|
type Balance = {
|
||||||
neo: Price;
|
neo: Price;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Price {
|
type Price = {
|
||||||
has: string;
|
has: string;
|
||||||
value: string;
|
value: string;
|
||||||
currency: Currency;
|
currency: Currency;
|
||||||
formatted: string;
|
formatted: string;
|
||||||
deadline: string;
|
deadline: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
type Currency = 'NEO';
|
type Currency = 'NEO';
|
||||||
|
|
||||||
interface Rating {
|
type Rating = {
|
||||||
value: string;
|
value: string;
|
||||||
count: string;
|
count: string;
|
||||||
my: string;
|
my: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ReadProgress {
|
type ReadProgress = {
|
||||||
chapter: string;
|
chapter: string;
|
||||||
progress: string;
|
progress: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ReadTime {
|
type ReadTime = {
|
||||||
hours: string;
|
hours: string;
|
||||||
minutes: string;
|
minutes: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface NovelsUser {
|
type NovelsUser = {
|
||||||
available: string;
|
available: string;
|
||||||
boosted: string;
|
boosted: string;
|
||||||
id: string;
|
id: string;
|
||||||
@@ -452,4 +452,4 @@ interface NovelsUser {
|
|||||||
isSubscription: string;
|
isSubscription: string;
|
||||||
isBlock: string;
|
isBlock: string;
|
||||||
gr: string;
|
gr: string;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ class novelOvh implements Plugin.PluginBase {
|
|||||||
|
|
||||||
export default new novelOvh();
|
export default new novelOvh();
|
||||||
|
|
||||||
interface BooksEntity {
|
type BooksEntity = {
|
||||||
id: string;
|
id: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
type: string;
|
type: string;
|
||||||
@@ -211,18 +211,18 @@ interface BooksEntity {
|
|||||||
chaptersCount: number;
|
chaptersCount: number;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
}
|
};
|
||||||
interface Name {
|
type Name = {
|
||||||
ru: string;
|
ru: string;
|
||||||
original: string;
|
original: string;
|
||||||
en: string;
|
en: string;
|
||||||
}
|
};
|
||||||
interface AltNamesEntity {
|
type AltNamesEntity = {
|
||||||
language: string;
|
language: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseNovel {
|
type responseNovel = {
|
||||||
book: Book;
|
book: Book;
|
||||||
branches: BranchesEntity[];
|
branches: BranchesEntity[];
|
||||||
chapters: ChaptersEntity[];
|
chapters: ChaptersEntity[];
|
||||||
@@ -233,8 +233,8 @@ interface responseNovel {
|
|||||||
promotionVideos?: null[] | null;
|
promotionVideos?: null[] | null;
|
||||||
folderItems?: null[] | null;
|
folderItems?: null[] | null;
|
||||||
contentNotices?: null[] | null;
|
contentNotices?: null[] | null;
|
||||||
}
|
};
|
||||||
interface Book {
|
type Book = {
|
||||||
id: string;
|
id: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
type: string;
|
type: string;
|
||||||
@@ -273,17 +273,17 @@ interface Book {
|
|||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
rating?: null;
|
rating?: null;
|
||||||
}
|
};
|
||||||
interface LabelsEntity {
|
type LabelsEntity = {
|
||||||
id: string;
|
id: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
};
|
||||||
interface RelationsEntity {
|
type RelationsEntity = {
|
||||||
type: string;
|
type: string;
|
||||||
publisher: Publisher;
|
publisher: Publisher;
|
||||||
}
|
};
|
||||||
interface Publisher {
|
type Publisher = {
|
||||||
id: string;
|
id: string;
|
||||||
kind: string;
|
kind: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
@@ -298,8 +298,8 @@ interface Publisher {
|
|||||||
tonAddress?: null;
|
tonAddress?: null;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
}
|
};
|
||||||
interface BranchesEntity {
|
type BranchesEntity = {
|
||||||
id: string;
|
id: string;
|
||||||
licensed: boolean;
|
licensed: boolean;
|
||||||
editorsChoice: boolean;
|
editorsChoice: boolean;
|
||||||
@@ -309,8 +309,8 @@ interface BranchesEntity {
|
|||||||
publishers?: PublishersEntity[] | null;
|
publishers?: PublishersEntity[] | null;
|
||||||
subscription?: null;
|
subscription?: null;
|
||||||
bookmark?: null;
|
bookmark?: null;
|
||||||
}
|
};
|
||||||
interface PublishersEntity {
|
type PublishersEntity = {
|
||||||
id: string;
|
id: string;
|
||||||
kind: string;
|
kind: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
@@ -324,8 +324,8 @@ interface PublishersEntity {
|
|||||||
tonAddress?: null;
|
tonAddress?: null;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
}
|
};
|
||||||
interface ChaptersEntity {
|
type ChaptersEntity = {
|
||||||
id: string;
|
id: string;
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
@@ -341,8 +341,8 @@ interface ChaptersEntity {
|
|||||||
createdAt: string;
|
createdAt: string;
|
||||||
expiredAt: string;
|
expiredAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
}
|
};
|
||||||
interface RelatedEntityOrBook {
|
type RelatedEntityOrBook = {
|
||||||
id: string;
|
id: string;
|
||||||
type: string;
|
type: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
@@ -361,23 +361,23 @@ interface RelatedEntityOrBook {
|
|||||||
averageRating: number;
|
averageRating: number;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
}
|
};
|
||||||
interface AssociatedEntity {
|
type AssociatedEntity = {
|
||||||
id: string;
|
id: string;
|
||||||
kind: string;
|
kind: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
book: RelatedEntityOrBook;
|
book: RelatedEntityOrBook;
|
||||||
}
|
};
|
||||||
interface CoversEntity {
|
type CoversEntity = {
|
||||||
id: string;
|
id: string;
|
||||||
volume: number;
|
volume: number;
|
||||||
image: string;
|
image: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseChapter {
|
type responseChapter = {
|
||||||
id: string;
|
id: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -397,22 +397,22 @@ interface responseChapter {
|
|||||||
createdAt: string;
|
createdAt: string;
|
||||||
expiredAt: string;
|
expiredAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
}
|
};
|
||||||
interface Content {
|
type Content = {
|
||||||
type: string;
|
type: string;
|
||||||
content?: ContentEntity[] | null;
|
content?: ContentEntity[] | null;
|
||||||
}
|
};
|
||||||
interface ContentEntity {
|
type ContentEntity = {
|
||||||
text?: string | null;
|
text?: string | null;
|
||||||
type: string;
|
type: string;
|
||||||
attrs?: Attrs | null;
|
attrs?: Attrs | null;
|
||||||
content?: Content[] | null;
|
content?: Content[] | null;
|
||||||
}
|
};
|
||||||
interface Attrs {
|
type Attrs = {
|
||||||
textAlign?: string;
|
textAlign?: string;
|
||||||
pages?: [string];
|
pages?: [string];
|
||||||
}
|
};
|
||||||
interface PagesEntity {
|
type PagesEntity = {
|
||||||
id: string;
|
id: string;
|
||||||
image: string;
|
image: string;
|
||||||
index: number;
|
index: number;
|
||||||
@@ -420,4 +420,4 @@ interface PagesEntity {
|
|||||||
height: number;
|
height: number;
|
||||||
width: number;
|
width: number;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -851,25 +851,25 @@ class TL implements Plugin.PluginBase {
|
|||||||
|
|
||||||
export default new TL();
|
export default new TL();
|
||||||
|
|
||||||
interface response {
|
type response = {
|
||||||
data: Data;
|
data: Data;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Data {
|
type Data = {
|
||||||
projects?: Projects;
|
projects?: Projects;
|
||||||
project?: Project;
|
project?: Project;
|
||||||
chapter?: DataChapter;
|
chapter?: DataChapter;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface DataChapter {
|
type DataChapter = {
|
||||||
text: Text;
|
text: Text;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Text {
|
type Text = {
|
||||||
text: string;
|
text: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Project {
|
type Project = {
|
||||||
title: string;
|
title: string;
|
||||||
translationStatus: string;
|
translationStatus: string;
|
||||||
fullUrl: string;
|
fullUrl: string;
|
||||||
@@ -879,58 +879,58 @@ interface Project {
|
|||||||
tags: Genre[];
|
tags: Genre[];
|
||||||
annotation: Text;
|
annotation: Text;
|
||||||
subprojects: Subprojects;
|
subprojects: Subprojects;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Cover {
|
type Cover = {
|
||||||
url: string;
|
url: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Genre {
|
type Genre = {
|
||||||
nameRu: string;
|
nameRu: string;
|
||||||
nameEng: string;
|
nameEng: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Person {
|
type Person = {
|
||||||
role: string;
|
role: string;
|
||||||
name: Name;
|
name: Name;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Name {
|
type Name = {
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Subprojects {
|
type Subprojects = {
|
||||||
content: SubprojectsContent[];
|
content: SubprojectsContent[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface SubprojectsContent {
|
type SubprojectsContent = {
|
||||||
title: null | string;
|
title: null | string;
|
||||||
volumes: Volumes;
|
volumes: Volumes;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Volumes {
|
type Volumes = {
|
||||||
content: VolumesContent[];
|
content: VolumesContent[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface VolumesContent {
|
type VolumesContent = {
|
||||||
shortName: null | string;
|
shortName: null | string;
|
||||||
chapters: ChapterElement[];
|
chapters: ChapterElement[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ChapterElement {
|
type ChapterElement = {
|
||||||
title: null | string;
|
title: null | string;
|
||||||
publishDate: Date;
|
publishDate: Date;
|
||||||
fullUrl: string;
|
fullUrl: string;
|
||||||
published: boolean;
|
published: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Projects {
|
type Projects = {
|
||||||
content: ProjectsContent[];
|
content: ProjectsContent[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ProjectsContent {
|
type ProjectsContent = {
|
||||||
title: string;
|
title: string;
|
||||||
fullUrl: string;
|
fullUrl: string;
|
||||||
covers: Cover[];
|
covers: Cover[];
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1147,7 +1147,7 @@ class RNBH implements Plugin.PluginBase {
|
|||||||
|
|
||||||
export default new RNBH();
|
export default new RNBH();
|
||||||
|
|
||||||
interface responseNovels {
|
type responseNovels = {
|
||||||
id: number;
|
id: number;
|
||||||
names: Names;
|
names: Names;
|
||||||
rating: number;
|
rating: number;
|
||||||
@@ -1158,27 +1158,27 @@ interface responseNovels {
|
|||||||
status: string;
|
status: string;
|
||||||
user?: User;
|
user?: User;
|
||||||
counts: Counts;
|
counts: Counts;
|
||||||
}
|
};
|
||||||
interface Names {
|
type Names = {
|
||||||
eng?: string;
|
eng?: string;
|
||||||
rus?: string;
|
rus?: string;
|
||||||
original: string;
|
original: string;
|
||||||
}
|
};
|
||||||
interface Poster {
|
type Poster = {
|
||||||
medium: string;
|
medium: string;
|
||||||
small: string;
|
small: string;
|
||||||
color: string;
|
color: string;
|
||||||
}
|
};
|
||||||
interface User {
|
type User = {
|
||||||
status?: null;
|
status?: null;
|
||||||
liked: boolean;
|
liked: boolean;
|
||||||
}
|
};
|
||||||
interface Counts {
|
type Counts = {
|
||||||
volumes: string;
|
volumes: string;
|
||||||
chapters: string;
|
chapters: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseNovel {
|
type responseNovel = {
|
||||||
id: number;
|
id: number;
|
||||||
names: Names;
|
names: Names;
|
||||||
rating: number;
|
rating: number;
|
||||||
@@ -1195,55 +1195,53 @@ interface responseNovel {
|
|||||||
start_reading_url: string;
|
start_reading_url: string;
|
||||||
html: string;
|
html: string;
|
||||||
tags: Tags;
|
tags: Tags;
|
||||||
}
|
};
|
||||||
interface Posters {
|
type Posters = {
|
||||||
big: string;
|
big: string;
|
||||||
medium: string;
|
medium: string;
|
||||||
small: string;
|
small: string;
|
||||||
tiny: string;
|
tiny: string;
|
||||||
color: string;
|
color: string;
|
||||||
}
|
};
|
||||||
interface AuthorsEntity {
|
type AuthorsEntity = {
|
||||||
name_eng: string;
|
name_eng: string;
|
||||||
pivot: Pivot;
|
pivot: Pivot;
|
||||||
}
|
};
|
||||||
interface Pivot {
|
type Pivot = {
|
||||||
ranobe_id: number;
|
ranobe_id: number;
|
||||||
author_id?: number;
|
author_id?: number;
|
||||||
translator_id?: number;
|
translator_id?: number;
|
||||||
}
|
};
|
||||||
interface TranslatorsEntity {
|
type TranslatorsEntity = {
|
||||||
name: string;
|
name: string;
|
||||||
pivot: Pivot;
|
pivot: Pivot;
|
||||||
}
|
};
|
||||||
interface Status {
|
type Status = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
}
|
name: string;
|
||||||
interface Tags {
|
};
|
||||||
|
type Tags = {
|
||||||
events?: GenresOrEntity[] | null;
|
events?: GenresOrEntity[] | null;
|
||||||
genres?: GenresOrEntity[] | null;
|
genres?: GenresOrEntity[] | null;
|
||||||
}
|
};
|
||||||
interface GenresOrEntity {
|
type GenresOrEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
names: Names;
|
names: Names;
|
||||||
url: string;
|
url: string;
|
||||||
title: string;
|
title: string;
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface VolumesEntity {
|
type VolumesEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
num: number;
|
num: number;
|
||||||
name: string;
|
name: string;
|
||||||
status: Status;
|
status: Status;
|
||||||
chapters?: ChaptersEntity[] | null;
|
chapters?: ChaptersEntity[] | null;
|
||||||
}
|
};
|
||||||
interface Status {
|
|
||||||
id: number;
|
type ChaptersEntity = {
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
interface ChaptersEntity {
|
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
num: number;
|
num: number;
|
||||||
@@ -1252,18 +1250,18 @@ interface ChaptersEntity {
|
|||||||
has_images: boolean;
|
has_images: boolean;
|
||||||
changed_at: string;
|
changed_at: string;
|
||||||
comments_count: string;
|
comments_count: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseSearch {
|
type responseSearch = {
|
||||||
meta: Meta;
|
meta: Meta;
|
||||||
collections?: DataEntity[] | null;
|
collections?: DataEntity[] | null;
|
||||||
data?: DataEntity[] | null;
|
data?: DataEntity[] | null;
|
||||||
}
|
};
|
||||||
interface Meta {
|
type Meta = {
|
||||||
key: string;
|
key: string;
|
||||||
title: string;
|
title: string;
|
||||||
}
|
};
|
||||||
interface DataEntity {
|
type DataEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
names?: Names | null;
|
names?: Names | null;
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
@@ -1275,10 +1273,10 @@ interface DataEntity {
|
|||||||
roles?: null[] | null;
|
roles?: null[] | null;
|
||||||
avatar?: Avatar | null;
|
avatar?: Avatar | null;
|
||||||
has_plus?: boolean | null;
|
has_plus?: boolean | null;
|
||||||
}
|
};
|
||||||
interface Avatar {
|
type Avatar = {
|
||||||
big: string;
|
big: string;
|
||||||
color: string;
|
color: string;
|
||||||
thumb: string;
|
thumb: string;
|
||||||
is_default: boolean;
|
is_default: boolean;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -526,82 +526,82 @@ function jsonToHtml(json: HTML[], images, html = '') {
|
|||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HTML {
|
type HTML = {
|
||||||
type: string;
|
type: string;
|
||||||
content?: HTML[];
|
content?: HTML[];
|
||||||
attrs?: Attrs;
|
attrs?: Attrs;
|
||||||
text?: string;
|
text?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Attrs {
|
type Attrs = {
|
||||||
src: string;
|
src: string;
|
||||||
alt: string | null;
|
alt: string | null;
|
||||||
title: string | null;
|
title: string | null;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface authorization {
|
type authorization = {
|
||||||
token: Token;
|
token: Token;
|
||||||
auth: Auth;
|
auth: Auth;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
}
|
};
|
||||||
interface Token {
|
type Token = {
|
||||||
token_type: string;
|
token_type: string;
|
||||||
expires_in: number;
|
expires_in: number;
|
||||||
access_token: string;
|
access_token: string;
|
||||||
refresh_token: string;
|
refresh_token: string;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
}
|
};
|
||||||
interface Auth {
|
type Auth = {
|
||||||
id: number;
|
id: number;
|
||||||
username: string;
|
username: string;
|
||||||
avatar: Cover;
|
avatar: Cover;
|
||||||
last_online_at: string;
|
last_online_at: string;
|
||||||
metadata: Metadata;
|
metadata: Metadata;
|
||||||
}
|
};
|
||||||
interface Metadata {
|
type Metadata = {
|
||||||
auth_domains: string;
|
auth_domains: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface TopLevel {
|
type TopLevel = {
|
||||||
data: DataClass | DataClass[];
|
data: DataClass | DataClass[];
|
||||||
links?: Links;
|
links?: Links;
|
||||||
meta?: Meta;
|
meta?: Meta;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface AgeRestriction {
|
type AgeRestriction = {
|
||||||
id: number;
|
id: number;
|
||||||
label: string;
|
label: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Branch {
|
type Branch = {
|
||||||
id: number;
|
id: number;
|
||||||
branch_id: null;
|
branch_id: null;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
teams: BranchTeam[];
|
teams: BranchTeam[];
|
||||||
user: User;
|
user: User;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface BranchTeam {
|
type BranchTeam = {
|
||||||
id: number;
|
id: number;
|
||||||
slug: string;
|
slug: string;
|
||||||
slug_url: string;
|
slug_url: string;
|
||||||
model: string;
|
model: string;
|
||||||
name: string;
|
name: string;
|
||||||
cover: Cover;
|
cover: Cover;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Cover {
|
type Cover = {
|
||||||
filename: null | string;
|
filename: null | string;
|
||||||
thumbnail: string;
|
thumbnail: string;
|
||||||
default: string;
|
default: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface User {
|
type User = {
|
||||||
username: string;
|
username: string;
|
||||||
id: number;
|
id: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface DataClass {
|
type DataClass = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
rus_name?: string;
|
rus_name?: string;
|
||||||
@@ -633,9 +633,9 @@ interface DataClass {
|
|||||||
likes_count?: number;
|
likes_count?: number;
|
||||||
content?: any;
|
content?: any;
|
||||||
attachments?: Attachment[];
|
attachments?: Attachment[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Artist {
|
type Artist = {
|
||||||
id: number;
|
id: number;
|
||||||
slug: string;
|
slug: string;
|
||||||
slug_url: string;
|
slug_url: string;
|
||||||
@@ -648,16 +648,16 @@ interface Artist {
|
|||||||
confirmed: null;
|
confirmed: null;
|
||||||
user_id: number;
|
user_id: number;
|
||||||
titles_count_details: null;
|
titles_count_details: null;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Subscription {
|
type Subscription = {
|
||||||
is_subscribed: boolean;
|
is_subscribed: boolean;
|
||||||
source_type: string;
|
source_type: string;
|
||||||
source_id: number;
|
source_id: number;
|
||||||
relation: null;
|
relation: null;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Attachment {
|
type Attachment = {
|
||||||
id?: string;
|
id?: string;
|
||||||
filename: string;
|
filename: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -665,14 +665,14 @@ interface Attachment {
|
|||||||
url: string;
|
url: string;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Genre {
|
type Genre = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface DataTeam {
|
type DataTeam = {
|
||||||
id: number;
|
id: number;
|
||||||
slug: string;
|
slug: string;
|
||||||
slug_url: string;
|
slug_url: string;
|
||||||
@@ -682,22 +682,22 @@ interface DataTeam {
|
|||||||
details?: Details;
|
details?: Details;
|
||||||
vk?: string;
|
vk?: string;
|
||||||
discord?: null;
|
discord?: null;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Details {
|
type Details = {
|
||||||
branch_id: null;
|
branch_id: null;
|
||||||
is_active: boolean;
|
is_active: boolean;
|
||||||
subscriptions_count: null;
|
subscriptions_count: null;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Links {
|
type Links = {
|
||||||
first: string;
|
first: string;
|
||||||
last: null;
|
last: null;
|
||||||
prev: null;
|
prev: null;
|
||||||
next: string;
|
next: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Meta {
|
type Meta = {
|
||||||
current_page?: number;
|
current_page?: number;
|
||||||
from?: number;
|
from?: number;
|
||||||
path?: string;
|
path?: string;
|
||||||
@@ -707,9 +707,9 @@ interface Meta {
|
|||||||
has_next_page?: boolean;
|
has_next_page?: boolean;
|
||||||
seed?: string;
|
seed?: string;
|
||||||
country?: string;
|
country?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface DataChapter {
|
type DataChapter = {
|
||||||
id: number;
|
id: number;
|
||||||
index: number;
|
index: number;
|
||||||
item_number: number;
|
item_number: number;
|
||||||
@@ -719,4 +719,4 @@ interface DataChapter {
|
|||||||
name: string;
|
name: string;
|
||||||
branches_count: number;
|
branches_count: number;
|
||||||
branches: Branch[];
|
branches: Branch[];
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ class RNRF implements Plugin.PluginBase {
|
|||||||
|
|
||||||
export default new RNRF();
|
export default new RNRF();
|
||||||
|
|
||||||
interface response {
|
type response = {
|
||||||
props: Props;
|
props: Props;
|
||||||
page: string;
|
page: string;
|
||||||
query: Query;
|
query: Query;
|
||||||
@@ -152,25 +152,25 @@ interface response {
|
|||||||
gssp: boolean;
|
gssp: boolean;
|
||||||
appGip: boolean;
|
appGip: boolean;
|
||||||
dynamicIds?: string[];
|
dynamicIds?: string[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Props {
|
type Props = {
|
||||||
pageProps: PageProps;
|
pageProps: PageProps;
|
||||||
isMobile: boolean;
|
isMobile: boolean;
|
||||||
theme: string;
|
theme: string;
|
||||||
__N_SSP: boolean;
|
__N_SSP: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface PageProps {
|
type PageProps = {
|
||||||
initialReduxState: InitialReduxState;
|
initialReduxState: InitialReduxState;
|
||||||
book?: PagePropsBook;
|
book?: PagePropsBook;
|
||||||
totalData?: TotalData;
|
totalData?: TotalData;
|
||||||
layoutViewCookies?: string;
|
layoutViewCookies?: string;
|
||||||
displayBanner?: boolean;
|
displayBanner?: boolean;
|
||||||
chapter?: PagePropsChapter;
|
chapter?: PagePropsChapter;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface PagePropsBook {
|
type PagePropsBook = {
|
||||||
id: number;
|
id: number;
|
||||||
countryId: number;
|
countryId: number;
|
||||||
creatorId: number;
|
creatorId: number;
|
||||||
@@ -215,9 +215,9 @@ interface PagePropsBook {
|
|||||||
verticalImage: Image;
|
verticalImage: Image;
|
||||||
horizontalImage: Image;
|
horizontalImage: Image;
|
||||||
squareImage: Image;
|
squareImage: Image;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ChapterElement {
|
type ChapterElement = {
|
||||||
id: number;
|
id: number;
|
||||||
bookId: number;
|
bookId: number;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -233,35 +233,35 @@ interface ChapterElement {
|
|||||||
price: number;
|
price: number;
|
||||||
views: number;
|
views: number;
|
||||||
isUserPaid?: boolean;
|
isUserPaid?: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Country {
|
type Country = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
code: string;
|
code: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Genre {
|
type Genre = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Image {
|
type Image = {
|
||||||
id: number;
|
id: number;
|
||||||
alt: string;
|
alt: string;
|
||||||
path: string;
|
path: string;
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Timer {
|
type Timer = {
|
||||||
value: null;
|
value: null;
|
||||||
for: null;
|
for: null;
|
||||||
message: string;
|
message: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface PagePropsChapter {
|
type PagePropsChapter = {
|
||||||
id: number;
|
id: number;
|
||||||
bookId: number;
|
bookId: number;
|
||||||
editorId: null;
|
editorId: null;
|
||||||
@@ -289,94 +289,94 @@ interface PagePropsChapter {
|
|||||||
nextChapter: null;
|
nextChapter: null;
|
||||||
previousChapter: PreviousChapter;
|
previousChapter: PreviousChapter;
|
||||||
corrections: null;
|
corrections: null;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ChapterBook {
|
type ChapterBook = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
titleEn: string;
|
titleEn: string;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Content {
|
type Content = {
|
||||||
id: number;
|
id: number;
|
||||||
text: string;
|
text: string;
|
||||||
symbolsCount: number;
|
symbolsCount: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface PreviousChapter {
|
type PreviousChapter = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
views: number;
|
views: number;
|
||||||
publishedAt: Date;
|
publishedAt: Date;
|
||||||
url: string;
|
url: string;
|
||||||
numberChapter: string;
|
numberChapter: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface InitialReduxState {
|
type InitialReduxState = {
|
||||||
data?: Data;
|
data?: Data;
|
||||||
auth: Auth;
|
auth: Auth;
|
||||||
banners?: Banners;
|
banners?: Banners;
|
||||||
corrections?: Corrections;
|
corrections?: Corrections;
|
||||||
readerSettings?: ReaderSettings;
|
readerSettings?: ReaderSettings;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Auth {
|
type Auth = {
|
||||||
isAuth: boolean;
|
isAuth: boolean;
|
||||||
user: null;
|
user: null;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Banners {
|
type Banners = {
|
||||||
displaySale: boolean;
|
displaySale: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Corrections {
|
type Corrections = {
|
||||||
items: null;
|
items: null;
|
||||||
data: null;
|
data: null;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Data {
|
type Data = {
|
||||||
bookPage: BookPage | null;
|
bookPage: BookPage | null;
|
||||||
chapterPage: ChapterPage | null;
|
chapterPage: ChapterPage | null;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface BookPage {
|
type BookPage = {
|
||||||
bookId: number;
|
bookId: number;
|
||||||
bookmark: null;
|
bookmark: null;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ChapterPage {
|
type ChapterPage = {
|
||||||
chapterId: number;
|
chapterId: number;
|
||||||
bookId: number;
|
bookId: number;
|
||||||
bookmark: null;
|
bookmark: null;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ReaderSettings {
|
type ReaderSettings = {
|
||||||
fontSize: number;
|
fontSize: number;
|
||||||
lineHeight: number;
|
lineHeight: number;
|
||||||
textIndent: number;
|
textIndent: number;
|
||||||
sidebarHide: number;
|
sidebarHide: number;
|
||||||
textAlign: string;
|
textAlign: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface TotalData {
|
type TotalData = {
|
||||||
items: Item[];
|
items: Item[];
|
||||||
_links: Links;
|
_links: Links;
|
||||||
pagesData: PagesData;
|
pagesData: PagesData;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Links {
|
type Links = {
|
||||||
self: First;
|
self: First;
|
||||||
first: First;
|
first: First;
|
||||||
last: First;
|
last: First;
|
||||||
next: First;
|
next: First;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface First {
|
type First = {
|
||||||
href: string;
|
href: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Item {
|
type Item = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
@@ -390,16 +390,16 @@ interface Item {
|
|||||||
verticalImage: Image;
|
verticalImage: Image;
|
||||||
horizontalImage: Image;
|
horizontalImage: Image;
|
||||||
squareImage: Image;
|
squareImage: Image;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface PagesData {
|
type PagesData = {
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
pageCount: number;
|
pageCount: number;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
perPage: number;
|
perPage: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Query {
|
type Query = {
|
||||||
book?: string;
|
book?: string;
|
||||||
chapter?: string;
|
chapter?: string;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -946,7 +946,7 @@ class ReN implements Plugin.PluginBase {
|
|||||||
|
|
||||||
export default new ReN();
|
export default new ReN();
|
||||||
|
|
||||||
interface responseNovels {
|
type responseNovels = {
|
||||||
id: number;
|
id: number;
|
||||||
secondary_name: string;
|
secondary_name: string;
|
||||||
main_name: string;
|
main_name: string;
|
||||||
@@ -969,15 +969,15 @@ interface responseNovels {
|
|||||||
categories?: ItemEntity[] | null;
|
categories?: ItemEntity[] | null;
|
||||||
rus_name?: string;
|
rus_name?: string;
|
||||||
en_name?: string;
|
en_name?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Img {
|
type Img = {
|
||||||
high?: string;
|
high?: string;
|
||||||
mid?: string;
|
mid?: string;
|
||||||
low: string;
|
low: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseNovel {
|
type responseNovel = {
|
||||||
id: number;
|
id: number;
|
||||||
img: Img;
|
img: Img;
|
||||||
secondary_name: string;
|
secondary_name: string;
|
||||||
@@ -1010,42 +1010,42 @@ interface responseNovel {
|
|||||||
uploaded: number;
|
uploaded: number;
|
||||||
can_post_comments: boolean;
|
can_post_comments: boolean;
|
||||||
adaptation: Adaptation | null;
|
adaptation: Adaptation | null;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Adaptation {
|
type Adaptation = {
|
||||||
main_name: string;
|
main_name: string;
|
||||||
secondary_name: string;
|
secondary_name: string;
|
||||||
dir: string;
|
dir: string;
|
||||||
img: string;
|
img: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ItemEntity {
|
type ItemEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
};
|
||||||
interface PublishersEntity {
|
type PublishersEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
img: string;
|
img: string;
|
||||||
dir: string;
|
dir: string;
|
||||||
tagline: string;
|
tagline: string;
|
||||||
type: string;
|
type: string;
|
||||||
}
|
};
|
||||||
interface BranchesEntity {
|
type BranchesEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
img: string;
|
img: string;
|
||||||
publishers?: PublishersEntity[] | null;
|
publishers?: PublishersEntity[] | null;
|
||||||
subscribed: boolean;
|
subscribed: boolean;
|
||||||
total_votes: number;
|
total_votes: number;
|
||||||
count_chapters: number;
|
count_chapters: number;
|
||||||
}
|
};
|
||||||
interface FirstChapter {
|
type FirstChapter = {
|
||||||
id: number;
|
id: number;
|
||||||
tome: number;
|
tome: number;
|
||||||
chapter: string;
|
chapter: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseСhapters {
|
type responseСhapters = {
|
||||||
id: number;
|
id: number;
|
||||||
rated?: null;
|
rated?: null;
|
||||||
viewed?: null;
|
viewed?: null;
|
||||||
@@ -1060,9 +1060,9 @@ interface responseСhapters {
|
|||||||
pub_date?: null;
|
pub_date?: null;
|
||||||
upload_date: string;
|
upload_date: string;
|
||||||
is_paid: boolean;
|
is_paid: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseСhapter {
|
type responseСhapter = {
|
||||||
id: number;
|
id: number;
|
||||||
chapter: string;
|
chapter: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -1078,4 +1078,4 @@ interface responseСhapter {
|
|||||||
index: number;
|
index: number;
|
||||||
publishers?: PublishersEntity[] | null;
|
publishers?: PublishersEntity[] | null;
|
||||||
tome: number;
|
tome: number;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ class RV implements Plugin.PluginBase {
|
|||||||
|
|
||||||
export default new RV();
|
export default new RV();
|
||||||
|
|
||||||
interface book {
|
type book = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
original_name: string;
|
original_name: string;
|
||||||
@@ -151,9 +151,9 @@ interface book {
|
|||||||
updated_at: string;
|
updated_at: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
chapters_count: number;
|
chapters_count: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface chapters {
|
type chapters = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
number: string;
|
number: string;
|
||||||
@@ -173,7 +173,7 @@ interface chapters {
|
|||||||
is_new: boolean;
|
is_new: boolean;
|
||||||
purchased_by_user: boolean;
|
purchased_by_user: boolean;
|
||||||
book_slug: string;
|
book_slug: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
function unicodeToUtf8(unicode: string) {
|
function unicodeToUtf8(unicode: string) {
|
||||||
return unicode.replace(/\\u([\d\w]{4})/gi, (match, hex) =>
|
return unicode.replace(/\\u([\d\w]{4})/gi, (match, hex) =>
|
||||||
|
|||||||
@@ -111,10 +111,10 @@ class SkyNovels implements Plugin.PluginBase {
|
|||||||
|
|
||||||
export default new SkyNovels();
|
export default new SkyNovels();
|
||||||
|
|
||||||
interface response {
|
type response = {
|
||||||
novels?: NovelsEntity[] | null;
|
novels?: NovelsEntity[] | null;
|
||||||
}
|
};
|
||||||
interface NovelsEntity {
|
type NovelsEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
nvl_author?: number | null;
|
nvl_author?: number | null;
|
||||||
nvl_content: string;
|
nvl_content: string;
|
||||||
@@ -135,16 +135,16 @@ interface NovelsEntity {
|
|||||||
nvl_rating?: number | null;
|
nvl_rating?: number | null;
|
||||||
nvl_ratings_count: number;
|
nvl_ratings_count: number;
|
||||||
genres?: GenresEntity[] | null;
|
genres?: GenresEntity[] | null;
|
||||||
}
|
};
|
||||||
interface GenresEntity {
|
type GenresEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
genre_name: string;
|
genre_name: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseBook {
|
type responseBook = {
|
||||||
novel?: NovelEntity[] | null;
|
novel?: NovelEntity[] | null;
|
||||||
}
|
};
|
||||||
interface NovelEntity {
|
type NovelEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
nvl_author: number;
|
nvl_author: number;
|
||||||
nvl_content: string;
|
nvl_content: string;
|
||||||
@@ -168,29 +168,29 @@ interface NovelEntity {
|
|||||||
novel_ratings?: NovelRatingsEntity[] | null;
|
novel_ratings?: NovelRatingsEntity[] | null;
|
||||||
collaborators?: CollaboratorsEntity[] | null;
|
collaborators?: CollaboratorsEntity[] | null;
|
||||||
genres?: GenresEntity[] | null;
|
genres?: GenresEntity[] | null;
|
||||||
}
|
};
|
||||||
interface BookmarksEntity {
|
type BookmarksEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
user_id: number;
|
user_id: number;
|
||||||
chp_id: number;
|
chp_id: number;
|
||||||
chp_name: string;
|
chp_name: string;
|
||||||
}
|
};
|
||||||
interface VolumesEntity {
|
type VolumesEntity = {
|
||||||
vlm_title: string;
|
vlm_title: string;
|
||||||
id: number;
|
id: number;
|
||||||
nvl_id: number;
|
nvl_id: number;
|
||||||
user_id?: number | null;
|
user_id?: number | null;
|
||||||
chapters?: ChaptersEntity[] | null;
|
chapters?: ChaptersEntity[] | null;
|
||||||
}
|
};
|
||||||
interface ChaptersEntity {
|
type ChaptersEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
chp_index_title: string;
|
chp_index_title: string;
|
||||||
chp_name: string;
|
chp_name: string;
|
||||||
chp_number: number;
|
chp_number: number;
|
||||||
chp_status: string;
|
chp_status: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
};
|
||||||
interface NovelRatingsEntity {
|
type NovelRatingsEntity = {
|
||||||
user_id: number;
|
user_id: number;
|
||||||
rate_value: number;
|
rate_value: number;
|
||||||
rate_comment: string;
|
rate_comment: string;
|
||||||
@@ -201,21 +201,21 @@ interface NovelRatingsEntity {
|
|||||||
user_login: string;
|
user_login: string;
|
||||||
image?: string | null;
|
image?: string | null;
|
||||||
likes?: (LikesEntity | null)[] | null;
|
likes?: (LikesEntity | null)[] | null;
|
||||||
}
|
};
|
||||||
interface LikesEntity {
|
type LikesEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
user_id: number;
|
user_id: number;
|
||||||
user_login: string;
|
user_login: string;
|
||||||
}
|
};
|
||||||
interface CollaboratorsEntity {
|
type CollaboratorsEntity = {
|
||||||
user_id: number;
|
user_id: number;
|
||||||
user_login: string;
|
user_login: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface responseChapter {
|
type responseChapter = {
|
||||||
chapter?: ChapterEntity[] | null;
|
chapter?: ChapterEntity[] | null;
|
||||||
}
|
};
|
||||||
interface ChapterEntity {
|
type ChapterEntity = {
|
||||||
id: number;
|
id: number;
|
||||||
chp_author: number;
|
chp_author: number;
|
||||||
chp_translator?: null;
|
chp_translator?: null;
|
||||||
@@ -237,9 +237,9 @@ interface ChapterEntity {
|
|||||||
comments?: null[] | null;
|
comments?: null[] | null;
|
||||||
reactions?: null[] | null;
|
reactions?: null[] | null;
|
||||||
total_reactions?: TotalReactionsEntity[] | null;
|
total_reactions?: TotalReactionsEntity[] | null;
|
||||||
}
|
};
|
||||||
interface TotalReactionsEntity {
|
type TotalReactionsEntity = {
|
||||||
reaction_id: number;
|
reaction_id: number;
|
||||||
reaction_name: string;
|
reaction_name: string;
|
||||||
reaction_count: number;
|
reaction_count: number;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ class LSHNovel implements Plugin.PluginBase {
|
|||||||
|
|
||||||
async searchNovels(
|
async searchNovels(
|
||||||
searchTerm: string,
|
searchTerm: string,
|
||||||
pageNo?: number | undefined,
|
//pageNo?: number | undefined,
|
||||||
): Promise<Plugin.NovelItem[]> {
|
): Promise<Plugin.NovelItem[]> {
|
||||||
const url = `${this.site}?s=${searchTerm}`;
|
const url = `${this.site}?s=${searchTerm}`;
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
|
|||||||
@@ -142,55 +142,55 @@ function jsonToHtml(json: HTML[], html = '') {
|
|||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface response {
|
type response = {
|
||||||
projects?: TopLevelProject[];
|
projects?: TopLevelProject[];
|
||||||
updates?: Update[];
|
updates?: Update[];
|
||||||
project?: TopLevelProject;
|
project?: TopLevelProject;
|
||||||
books?: BookElement[];
|
books?: BookElement[];
|
||||||
chapter?: TopLevelChapter;
|
chapter?: TopLevelChapter;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface BookElement {
|
type BookElement = {
|
||||||
id: number;
|
id: number;
|
||||||
rank: number;
|
rank: number;
|
||||||
title: string;
|
title: string;
|
||||||
chapters: PurpleChapter[];
|
chapters: PurpleChapter[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface PurpleChapter {
|
type PurpleChapter = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
rank: string;
|
rank: string;
|
||||||
modifiedAt: Date;
|
modifiedAt: Date;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface TopLevelChapter {
|
type TopLevelChapter = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
rank: string;
|
rank: string;
|
||||||
content: string;
|
content: string;
|
||||||
modifiedAt: Date;
|
modifiedAt: Date;
|
||||||
book: ChapterBook;
|
book: ChapterBook;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ChapterBook {
|
type ChapterBook = {
|
||||||
id: number;
|
id: number;
|
||||||
rank: number;
|
rank: number;
|
||||||
title: string;
|
title: string;
|
||||||
chapters: FluffyChapter[];
|
chapters: FluffyChapter[];
|
||||||
project: BookProject;
|
project: BookProject;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface FluffyChapter {
|
type FluffyChapter = {
|
||||||
id: number;
|
id: number;
|
||||||
rank: string;
|
rank: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface BookProject {
|
type BookProject = {
|
||||||
id: number;
|
id: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface TopLevelProject {
|
type TopLevelProject = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
@@ -205,37 +205,37 @@ interface TopLevelProject {
|
|||||||
image: Image;
|
image: Image;
|
||||||
tags?: Genre[];
|
tags?: Genre[];
|
||||||
genres?: Genre[];
|
genres?: Genre[];
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Genre {
|
type Genre = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Image {
|
type Image = {
|
||||||
id: number;
|
id: number;
|
||||||
url: string;
|
url: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Update {
|
type Update = {
|
||||||
id: number;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
author: string;
|
author: string;
|
||||||
translator: string;
|
translator: string;
|
||||||
modifiedAt: Date;
|
modifiedAt: Date;
|
||||||
image: Image;
|
image: Image;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface HTML {
|
type HTML = {
|
||||||
type: string;
|
type: string;
|
||||||
content?: HTML[];
|
content?: HTML[];
|
||||||
attrs?: Attrs;
|
attrs?: Attrs;
|
||||||
text?: string;
|
text?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface Attrs {
|
type Attrs = {
|
||||||
src: string;
|
src: string;
|
||||||
alt: string | null;
|
alt: string | null;
|
||||||
title: string | null;
|
title: string | null;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -4,21 +4,21 @@ import { Plugin } from '@typings/plugin';
|
|||||||
import { FilterTypes, Filters } from '@libs/filterInputs';
|
import { FilterTypes, Filters } from '@libs/filterInputs';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
|
|
||||||
interface VolumeData {
|
type VolumeData = {
|
||||||
story: string;
|
story: string;
|
||||||
navigation: string;
|
navigation: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface ChapterSelect {
|
type ChapterSelect = {
|
||||||
chap_selector: string;
|
chap_selector: string;
|
||||||
eps_selector: boolean | string;
|
eps_selector: boolean | string;
|
||||||
}
|
};
|
||||||
|
|
||||||
interface VolumeList {
|
type VolumeList = {
|
||||||
chapters: Plugin.ChapterItem[];
|
chapters: Plugin.ChapterItem[];
|
||||||
volumes?: VolumeData[];
|
volumes?: VolumeData[];
|
||||||
}
|
};
|
||||||
|
|
||||||
class TruyenConect implements Plugin.PagePlugin {
|
class TruyenConect implements Plugin.PagePlugin {
|
||||||
id = 'truyenconect';
|
id = 'truyenconect';
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import { Plugin } from '@typings/plugin';
|
|||||||
import { Filters } from '@libs/filterInputs';
|
import { Filters } from '@libs/filterInputs';
|
||||||
import { NovelStatus } from '@libs/novelStatus';
|
import { NovelStatus } from '@libs/novelStatus';
|
||||||
|
|
||||||
interface SearchedNovel {
|
type SearchedNovel = {
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
coverUrl: string;
|
coverUrl: string;
|
||||||
}
|
};
|
||||||
interface SearchedResult {
|
type SearchedResult = {
|
||||||
data?: SearchedNovel[];
|
data?: SearchedNovel[];
|
||||||
}
|
};
|
||||||
|
|
||||||
class LightNovelVN implements Plugin.PagePlugin {
|
class LightNovelVN implements Plugin.PagePlugin {
|
||||||
id = 'lightnovel.vn';
|
id = 'lightnovel.vn';
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
import { AppState } from '@redux/store';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
// import { useAppStore } from '@store';
|
||||||
|
|
||||||
export default function DialogProvider() {
|
export default function DialogProvider() {
|
||||||
const activeDialogs = useSelector(
|
// const _activeDialogs = useAppStore(state => state.activeDialogs);
|
||||||
(state: AppState) => state.dialog.activeDialogs,
|
|
||||||
);
|
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import DialogProvider from './DialogProvider';
|
import DialogProvider from './DialogProvider';
|
||||||
import { Backdrop, Box, CircularProgress, Container } from '@mui/material';
|
import { Backdrop, Box, CircularProgress, Container } from '@mui/material';
|
||||||
import { useSelector } from 'react-redux';
|
import { useAppStore } from '@store';
|
||||||
import { AppState } from '@redux/store';
|
|
||||||
|
|
||||||
export default function AppProvider() {
|
export default function AppProvider() {
|
||||||
const loading = useSelector((state: AppState) => state.overlay.loading);
|
const loading = useAppStore(state => state.loading);
|
||||||
return (
|
return (
|
||||||
<Container sx={{ position: 'absolute' }}>
|
<Container sx={{ position: 'absolute' }}>
|
||||||
<Backdrop
|
<Backdrop
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
|
||||||
|
|
||||||
export enum EDialog {}
|
|
||||||
|
|
||||||
interface DialogState {
|
|
||||||
activeDialogs: EDialog[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialState: DialogState = { activeDialogs: [] };
|
|
||||||
|
|
||||||
export const dialogSlice = createSlice({
|
|
||||||
name: 'dialog',
|
|
||||||
initialState: initialState,
|
|
||||||
reducers: {
|
|
||||||
showDialog(state, action: PayloadAction<EDialog>) {
|
|
||||||
state.activeDialogs = [...state.activeDialogs, action.payload];
|
|
||||||
},
|
|
||||||
hideDialog(state, action: PayloadAction<EDialog>) {
|
|
||||||
state.activeDialogs = state.activeDialogs.filter(
|
|
||||||
dialog => dialog !== action.payload,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
hideAllDialogs(state) {
|
|
||||||
state.activeDialogs = [];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const { showDialog, hideDialog } = dialogSlice.actions;
|
|
||||||
|
|
||||||
export default dialogSlice.reducer;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { createSlice } from '@reduxjs/toolkit';
|
|
||||||
|
|
||||||
const initialState = {
|
|
||||||
loading: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const overlaySlice = createSlice({
|
|
||||||
name: 'overlay',
|
|
||||||
initialState: initialState,
|
|
||||||
reducers: {
|
|
||||||
showLoading(state) {
|
|
||||||
state.loading = true;
|
|
||||||
},
|
|
||||||
hideLoading(state) {
|
|
||||||
state.loading = false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const { showLoading, hideLoading } = overlaySlice.actions;
|
|
||||||
|
|
||||||
export default overlaySlice.reducer;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
|
||||||
import { Plugin } from '@typings/plugin';
|
|
||||||
|
|
||||||
const initialState: {
|
|
||||||
selected?: Plugin.PluginItem;
|
|
||||||
} = {
|
|
||||||
selected: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const pluginSlice = createSlice({
|
|
||||||
name: 'plugin',
|
|
||||||
initialState: initialState,
|
|
||||||
reducers: {
|
|
||||||
selectPlugin(state, acion: PayloadAction<Plugin.PluginItem>) {
|
|
||||||
state.selected = acion.payload;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const { selectPlugin } = pluginSlice.actions;
|
|
||||||
|
|
||||||
export default pluginSlice.reducer;
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { configureStore } from '@reduxjs/toolkit';
|
|
||||||
import dialogReducer from './dialogSlice';
|
|
||||||
import overlaySlice from './overlaySlice';
|
|
||||||
import pluginReducer from './pluginSlice';
|
|
||||||
|
|
||||||
const store = configureStore({
|
|
||||||
reducer: {
|
|
||||||
dialog: dialogReducer,
|
|
||||||
overlay: overlaySlice,
|
|
||||||
plugin: pluginReducer,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default store;
|
|
||||||
export type AppState = ReturnType<typeof store.getState>;
|
|
||||||
export type AppDispatch = typeof store.dispatch;
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { StoreCreator } from '.';
|
||||||
|
|
||||||
|
export enum EDialog {}
|
||||||
|
|
||||||
|
export type DialogStore = {
|
||||||
|
activeDialogs: EDialog[];
|
||||||
|
showDialog(dialog: EDialog): void;
|
||||||
|
hideDialog(dialog: EDialog): void;
|
||||||
|
hideAllDialogs(): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param set Use this inside actions to change the state
|
||||||
|
* @param get Use this inside actions to get the current state
|
||||||
|
*/
|
||||||
|
export const DialogStore: StoreCreator<DialogStore> = set => ({
|
||||||
|
// this is initial state
|
||||||
|
activeDialogs: [],
|
||||||
|
|
||||||
|
// those are actions
|
||||||
|
showDialog(dialog: EDialog) {
|
||||||
|
set(state => ({
|
||||||
|
...state,
|
||||||
|
activeDialogs: [...state.activeDialogs, dialog],
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
hideDialog(dialog) {
|
||||||
|
set(state => ({
|
||||||
|
...state,
|
||||||
|
activeDialogs: state.activeDialogs.filter(d => d !== dialog),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
hideAllDialogs() {
|
||||||
|
set(state => ({
|
||||||
|
...state,
|
||||||
|
activeDialogs: [],
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { create, StateCreator } from 'zustand';
|
||||||
|
import { PluginStore } from './pluginStore';
|
||||||
|
import { DialogStore } from './dialogStore';
|
||||||
|
import { OverlayStore } from './overlayStore';
|
||||||
|
|
||||||
|
export type AppStore = DialogStore &
|
||||||
|
OverlayStore &
|
||||||
|
PluginStore & { useSwitches: boolean; setUseSwitches(value: boolean): void };
|
||||||
|
|
||||||
|
// Helper types to use "slicing" like in Redux... We could just not use slicing, but eh
|
||||||
|
export type SetStore = Parameters<StateCreator<AppStore>>[0];
|
||||||
|
export type GetStore = Parameters<StateCreator<AppStore>>[1];
|
||||||
|
export type StoreCreator<T> = (s: SetStore, g: GetStore) => T;
|
||||||
|
|
||||||
|
export const useAppStore = create<AppStore>((set: SetStore, get: GetStore) => ({
|
||||||
|
...DialogStore(set, get),
|
||||||
|
...OverlayStore(set, get),
|
||||||
|
...PluginStore(set, get),
|
||||||
|
useSwitches: !!localStorage.getItem('useSwitches'),
|
||||||
|
setUseSwitches(uS: boolean) {
|
||||||
|
set(state => ({
|
||||||
|
...state,
|
||||||
|
useSwitches: uS,
|
||||||
|
}));
|
||||||
|
if (uS) localStorage.setItem('useSwitches', 'true');
|
||||||
|
else localStorage.removeItem('useSwitches');
|
||||||
|
},
|
||||||
|
}));
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { StoreCreator } from '.';
|
||||||
|
|
||||||
|
export type OverlayStore = {
|
||||||
|
loading: boolean;
|
||||||
|
showLoading(): void;
|
||||||
|
hideLoading(): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param set Use this inside actions to change the state
|
||||||
|
* @param get Use this inside actions to get the current state
|
||||||
|
*/
|
||||||
|
export const OverlayStore: StoreCreator<OverlayStore> = set => ({
|
||||||
|
// this is initial state
|
||||||
|
loading: false,
|
||||||
|
|
||||||
|
// those are actions
|
||||||
|
showLoading() {
|
||||||
|
set(state => ({
|
||||||
|
...state,
|
||||||
|
loading: true,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
hideLoading() {
|
||||||
|
set(state => ({
|
||||||
|
...state,
|
||||||
|
loading: false,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Plugin } from '@typings/plugin';
|
||||||
|
import { StoreCreator } from '.';
|
||||||
|
import { getPlugin } from '@provider/plugins';
|
||||||
|
|
||||||
|
export type PluginStore = {
|
||||||
|
pluginItem?: Plugin.PluginItem;
|
||||||
|
plugin?: Plugin.PluginBase;
|
||||||
|
selectPlugin(plugin: Plugin.PluginItem): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param set State setter for use inside actions
|
||||||
|
* @param get State getter for use inside actions, outside of State setter
|
||||||
|
*/
|
||||||
|
export const PluginStore: StoreCreator<PluginStore> = set => ({
|
||||||
|
// this is initial state
|
||||||
|
// selectedPlugin: undefined,
|
||||||
|
|
||||||
|
// those are actions
|
||||||
|
selectPlugin(pluginItem) {
|
||||||
|
set(state => ({
|
||||||
|
...state,
|
||||||
|
pluginItem,
|
||||||
|
plugin: getPlugin(pluginItem.id),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
});
|
||||||
+26
-27
@@ -1,6 +1,6 @@
|
|||||||
import { FilterToValues, Filters } from '@libs/filterInputs';
|
import { FilterToValues, Filters } from '@libs/filterInputs';
|
||||||
export namespace Plugin {
|
export namespace Plugin {
|
||||||
export interface ChapterItem {
|
export type ChapterItem = {
|
||||||
name: string;
|
name: string;
|
||||||
path: string;
|
path: string;
|
||||||
/**
|
/**
|
||||||
@@ -17,13 +17,13 @@ export namespace Plugin {
|
|||||||
* For novel without pages only
|
* For novel without pages only
|
||||||
*/
|
*/
|
||||||
page?: string;
|
page?: string;
|
||||||
}
|
};
|
||||||
export interface NovelItem {
|
export type NovelItem = {
|
||||||
name: string;
|
name: string;
|
||||||
path: string;
|
path: string;
|
||||||
cover?: string;
|
cover?: string;
|
||||||
}
|
};
|
||||||
export interface SourceNovel extends NovelItem {
|
export type SourceNovel = {
|
||||||
/** Comma separated genre list -> "action,fantasy,romance" */
|
/** Comma separated genre list -> "action,fantasy,romance" */
|
||||||
genres?: string;
|
genres?: string;
|
||||||
summary?: string;
|
summary?: string;
|
||||||
@@ -31,32 +31,32 @@ export namespace Plugin {
|
|||||||
artist?: string;
|
artist?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
chapters?: ChapterItem[];
|
chapters?: ChapterItem[];
|
||||||
}
|
} & NovelItem;
|
||||||
|
|
||||||
export interface SourcePage {
|
export type SourcePage = {
|
||||||
chapters: ChapterItem[];
|
chapters: ChapterItem[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface PopularNovelsOptions<
|
export type PopularNovelsOptions<
|
||||||
Q extends Filters | undefined = Filters | undefined,
|
Q extends Filters | undefined = Filters | undefined,
|
||||||
> {
|
> = {
|
||||||
showLatestNovels?: boolean;
|
showLatestNovels?: boolean;
|
||||||
filters?: Q extends undefined ? undefined : FilterToValues<Q>;
|
filters: Q extends undefined ? undefined : FilterToValues<Q>;
|
||||||
}
|
};
|
||||||
export interface PluginItem {
|
export type PluginItem = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
version: string;
|
version: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
site: string;
|
site: string;
|
||||||
}
|
};
|
||||||
export interface ImageRequestInit {
|
export type ImageRequestInit = {
|
||||||
method?: string;
|
method?: string;
|
||||||
headers?: Record<string, string>;
|
headers?: Record<string, string>;
|
||||||
body?: string;
|
body?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface PluginBase {
|
export type PluginBase = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
/**
|
/**
|
||||||
@@ -87,32 +87,31 @@ export namespace Plugin {
|
|||||||
parseChapter(chapterPath: string): Promise<string>;
|
parseChapter(chapterPath: string): Promise<string>;
|
||||||
searchNovels(searchTerm: string, pageNo: number): Promise<NovelItem[]>;
|
searchNovels(searchTerm: string, pageNo: number): Promise<NovelItem[]>;
|
||||||
resolveUrl?(path: string, isNovel?: boolean): string;
|
resolveUrl?(path: string, isNovel?: boolean): string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface PagePlugin extends PluginBase {
|
export type PagePlugin = {
|
||||||
parseNovel(
|
parseNovel(
|
||||||
novelPath: string,
|
novelPath: string,
|
||||||
): Promise<SourceNovel & { totalPages: number }>;
|
): Promise<SourceNovel & { totalPages: number }>;
|
||||||
parsePage(novelPath: string, page: string): Promise<SourcePage>;
|
parsePage(novelPath: string, page: string): Promise<SourcePage>;
|
||||||
}
|
} & PluginBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace HTMLParser2Util {
|
export namespace HTMLParser2Util {
|
||||||
interface HandlerBase {
|
type HandlerBase = {
|
||||||
onopentag?(name: string, attribs: Record<string, string>): void;
|
onopentag?(name: string, attribs: Record<string, string>): void;
|
||||||
ontext?(data: string): void;
|
ontext?(data: string): void;
|
||||||
onclosetag?(name: string, isImplied: boolean): void;
|
onclosetag?(name: string, isImplied: boolean): void;
|
||||||
}
|
};
|
||||||
|
|
||||||
export interface Handler extends HandlerBase {
|
export type Handler = {
|
||||||
isStarted?: boolean;
|
isStarted?: boolean;
|
||||||
isDone?: boolean;
|
isDone?: boolean;
|
||||||
}
|
} & HandlerBase;
|
||||||
|
|
||||||
// route htmlparser2 event to handlers
|
// route htmlparser2 event to handlers
|
||||||
export interface HandlerRouter<ActionType extends string>
|
export type HandlerRouter<ActionType extends string> = {
|
||||||
extends HandlerBase {
|
|
||||||
handlers: Record<ActionType, Handler | undefined>;
|
handlers: Record<ActionType, Handler | undefined>;
|
||||||
action: ActionType;
|
action: ActionType;
|
||||||
}
|
} & HandlerBase;
|
||||||
}
|
}
|
||||||
|
|||||||
+118
-117
@@ -1,128 +1,129 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||||
|
|
||||||
/* Projects */
|
/* Projects */
|
||||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||||
|
|
||||||
/* Language and Environment */
|
/* Language and Environment */
|
||||||
"target": "ES5" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
"target": "ES5" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||||
// "lib": [
|
// "lib": [
|
||||||
// "es2016",
|
// "es2016",
|
||||||
// "dom",
|
// "dom",
|
||||||
// "es5"
|
// "es5"
|
||||||
// ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
|
// ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
|
||||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||||
|
|
||||||
/* Modules */
|
/* Modules */
|
||||||
"module": "ES2020" /* Specify what module code is generated. */,
|
"module": "ES2020" /* Specify what module code is generated. */,
|
||||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||||
"moduleResolution": "Bundler" /* Specify how TypeScript looks up a file from a given module specifier. */,
|
"moduleResolution": "Bundler" /* Specify how TypeScript looks up a file from a given module specifier. */,
|
||||||
"baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
"baseUrl": "./" /* Specify the base directory to resolve non-relative module names. */,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@libs/*": ["./src/libs/*"],
|
"@libs/*": ["./src/libs/*"],
|
||||||
"@icons/*": ["./src/icons/*"],
|
"@icons/*": ["./src/icons/*"],
|
||||||
"@components/*": ["./src/components/*"],
|
"@components/*": ["./src/components/*"],
|
||||||
"@redux/*": ["./src/redux/*"],
|
"@redux/*": ["./src/redux/*"],
|
||||||
"@provider/*": ["./src/provider/*"],
|
"@provider/*": ["./src/provider/*"],
|
||||||
"@plugins/*": ["./src/plugins/*"],
|
"@plugins/*": ["./src/plugins/*"],
|
||||||
"@hooks/*": ["./src/hooks/*"],
|
"@hooks/*": ["./src/hooks/*"],
|
||||||
"@scripts/*": ["./scripts/*"],
|
"@scripts/*": ["./scripts/*"],
|
||||||
"@typings/*": ["./src/types/*"]
|
"@typings/*": ["./src/types/*"],
|
||||||
} /* Specify a set of entries that re-map imports to additional lookup locations. */,
|
"@store": ["./src/store"]
|
||||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
} /* Specify a set of entries that re-map imports to additional lookup locations. */,
|
||||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||||
"resolveJsonModule": true /* Enable importing .json files. */,
|
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
"resolveJsonModule": true /* Enable importing .json files. */,
|
||||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||||
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||||
|
|
||||||
/* JavaScript Support */
|
/* JavaScript Support */
|
||||||
// "allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */,
|
// "allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */,
|
||||||
// "checkJs": true /* Enable error reporting in type-checked JavaScript files. */,
|
// "checkJs": true /* Enable error reporting in type-checked JavaScript files. */,
|
||||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||||
|
|
||||||
/* Emit */
|
/* Emit */
|
||||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||||
"outDir": "./.js/" /* Specify an output folder for all emitted files. */,
|
"outDir": "./.js/" /* Specify an output folder for all emitted files. */,
|
||||||
// "removeComments": true, /* Disable emitting comments. */
|
// "removeComments": true, /* Disable emitting comments. */
|
||||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"checkJs": false,
|
"checkJs": false,
|
||||||
|
|
||||||
/* Interop Constraints */
|
/* Interop Constraints */
|
||||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
||||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||||
|
|
||||||
/* Type Checking */
|
/* Type Checking */
|
||||||
"strict": true /* Enable all strict type-checking options. */,
|
"strict": true /* Enable all strict type-checking options. */,
|
||||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||||
|
|
||||||
/* Completeness */
|
/* Completeness */
|
||||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||||
|
|
||||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||||
},
|
},
|
||||||
"exclude": ["./src/plugins/**/*.broken.ts", "./.js/**/*", "scripts", "docs"]
|
"exclude": ["./src/plugins/**/*.broken.ts", "./.js/**/*", "scripts", "docs"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export default defineConfig({
|
|||||||
'@hooks': path.resolve(dirname, './src/hooks'),
|
'@hooks': path.resolve(dirname, './src/hooks'),
|
||||||
'@typings': path.resolve(dirname, './src/types'),
|
'@typings': path.resolve(dirname, './src/types'),
|
||||||
'@scripts': path.resolve(dirname, './scripts'),
|
'@scripts': path.resolve(dirname, './scripts'),
|
||||||
|
'@store': path.resolve(dirname, './src/store'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
|
|||||||
Reference in New Issue
Block a user