diff --git a/.gitignore b/.gitignore
index 3c07993..fd49400 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@ src/plugins/*/*\[*\]*.ts
dist
total.svg
src/plugins/index.ts
+.history
\ No newline at end of file
diff --git a/eslint.config.js b/eslint.config.js
index a88944d..1393852 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -138,13 +138,14 @@ export default tseslint.config(
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/ban-ts-comment': 'off',
+ '@typescript-eslint/consistent-type-definitions': ['error', 'type'],
'no-case-declarations': 'warn',
'no-undef': 'error',
},
languageOptions: {
ecmaVersion: 5,
sourceType: 'module',
- globals: Object.assign(...globalsHermes),
+ globals: Object.assign({}, ...globalsHermes),
},
},
{
@@ -154,6 +155,7 @@ export default tseslint.config(
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-namespace': 'off',
+ '@typescript-eslint/consistent-type-definitions': ['error', 'type'],
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/ban-ts-comment': 'off',
'no-undef': 'error',
diff --git a/package-lock.json b/package-lock.json
index 92618ed..8ca8ed9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,11 +26,13 @@
"react-virtuoso": "^4.7.11",
"react-window": "^1.8.10",
"sanitize-html": "^2.12.1",
- "urlencode": "^2.0.0"
+ "urlencode": "^2.0.0",
+ "zustand": "^4.5.4"
},
"devDependencies": {
"@eslint/js": "^9.5.0",
"@types/eslint__js": "^8.42.3",
+ "@types/eslint-config-prettier": "^6.11.3",
"@types/qs": "^6.9.15",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
@@ -1868,6 +1870,12 @@
"@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": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
@@ -5661,6 +5669,41 @@
"funding": {
"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": {
@@ -6763,6 +6806,12 @@
"@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": {
"version": "1.0.5",
"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",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"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": {}
+ }
+ }
}
}
}
diff --git a/package.json b/package.json
index 8713ecc..b61cc31 100644
--- a/package.json
+++ b/package.json
@@ -39,11 +39,13 @@
"react-virtuoso": "^4.7.11",
"react-window": "^1.8.10",
"sanitize-html": "^2.12.1",
- "urlencode": "^2.0.0"
+ "urlencode": "^2.0.0",
+ "zustand": "^4.5.4"
},
"devDependencies": {
"@eslint/js": "^9.5.0",
"@types/eslint__js": "^8.42.3",
+ "@types/eslint-config-prettier": "^6.11.3",
"@types/qs": "^6.9.15",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
diff --git a/proxy_server.js b/proxy_server.js
index a0846d9..f23cad6 100644
--- a/proxy_server.js
+++ b/proxy_server.js
@@ -26,6 +26,7 @@ const proxyRequest = (req, res) => {
proxy.on('proxyRes', function (proxyRes, req, res) {
var statusCode = proxyRes.statusCode;
+ // Redirect
if (
statusCode === 301 ||
statusCode === 302 ||
@@ -82,7 +83,7 @@ const cookiesHandler = (req, res) => {
http
.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']) {
res.setHeader(
'access-control-allow-methods',
diff --git a/scripts/multisrc/hotnovelpub/filter_refresh.ts b/scripts/multisrc/hotnovelpub/filter_refresh.ts
index 2ede94b..3d6f01d 100644
--- a/scripts/multisrc/hotnovelpub/filter_refresh.ts
+++ b/scripts/multisrc/hotnovelpub/filter_refresh.ts
@@ -55,16 +55,16 @@ async function getFilters(sources: HotNovelPubMetadata) {
return filters;
}
-interface response {
+type response = {
status: number;
message: string;
data?: DataEntity[];
-}
-interface DataEntity {
+};
+type DataEntity = {
id: number;
name: string;
slug: string;
-}
+};
async function start() {
const result = [];
diff --git a/scripts/multisrc/hotnovelpub/template.ts b/scripts/multisrc/hotnovelpub/template.ts
index a0ee06b..8eee869 100644
--- a/scripts/multisrc/hotnovelpub/template.ts
+++ b/scripts/multisrc/hotnovelpub/template.ts
@@ -3,17 +3,17 @@ import { Filters, FilterTypes } from '@libs/filterInputs';
import { Plugin } from '@typings/plugin';
import { NovelStatus } from '@libs/novelStatus';
-export interface HotNovelPubMetadata {
+export type HotNovelPubMetadata = {
id: string;
sourceSite: string;
sourceName: string;
filters?: Filters;
options?: HotNovelPubOptions;
-}
+};
-interface HotNovelPubOptions {
+type HotNovelPubOptions = {
lang?: string;
-}
+};
class HotNovelPubPlugin implements Plugin.PluginBase {
id: string;
@@ -161,16 +161,16 @@ class HotNovelPubPlugin implements Plugin.PluginBase {
resolveUrl = (path: string) => this.site + '/' + path;
}
-interface responseNovels {
+type responseNovels = {
status: number;
message: string;
data: Data;
-}
-interface Data {
+};
+type Data = {
category?: Category;
books: Books;
-}
-interface Category {
+};
+type Category = {
id: number;
name: string;
description: string;
@@ -178,13 +178,13 @@ interface Category {
createdAt: string;
updatedAt: string;
countryId: number;
-}
-interface Books {
+};
+type Books = {
total: number;
pages_count: number;
data?: DataEntity[];
-}
-interface DataEntity {
+};
+type DataEntity = {
id: number;
name: string;
view: number;
@@ -193,24 +193,24 @@ interface DataEntity {
slug: string;
categories?: CategoriesEntity[] | null;
source?: null;
-}
-interface CategoriesEntity {
+};
+type CategoriesEntity = {
id: number;
name: string;
slug?: string;
-}
+};
-interface responseNovel {
+type responseNovel = {
status: number;
message: string;
data: Data1;
-}
-interface Data1 {
+};
+type Data1 = {
book: Book1;
tags: Tags;
chapters?: ChaptersEntity[];
-}
-interface Book1 {
+};
+type Book1 = {
id: number;
name: string;
description: string;
@@ -229,47 +229,47 @@ interface Book1 {
source?: null;
idSource?: null;
prize?: null;
-}
-interface Authorize {
+};
+type Authorize = {
id: number;
name: string;
description: string;
avatar: string;
slug: string;
-}
-interface Tags {
+};
+type Tags = {
tags_name?: string[] | null;
tags?: CategoriesEntity[] | null;
-}
-interface ChaptersEntity {
+};
+type ChaptersEntity = {
id: number;
title: string;
slug: string;
index: number;
-}
+};
-interface ChapterType {
+type ChapterType = {
type: string;
data?: string[];
-}
+};
-interface responseSearch {
+type responseSearch = {
status: number;
message: string;
data: Data2;
-}
-interface Data2 {
+};
+type Data2 = {
books?: BooksEntity[] | null;
authorizes?: AuthorizesEntityOrCategoriesEntity[] | null;
categories?: AuthorizesEntityOrCategoriesEntity[] | null;
-}
-interface BooksEntity {
+};
+type BooksEntity = {
id: number;
name: string;
slug: string;
-}
-interface AuthorizesEntityOrCategoriesEntity {
+};
+type AuthorizesEntityOrCategoriesEntity = {
id: number;
name: string;
slug: string;
-}
+};
diff --git a/scripts/multisrc/ifreedom/template.ts b/scripts/multisrc/ifreedom/template.ts
index afda4c8..88f3d40 100644
--- a/scripts/multisrc/ifreedom/template.ts
+++ b/scripts/multisrc/ifreedom/template.ts
@@ -5,12 +5,12 @@ import { NovelStatus } from '@libs/novelStatus';
import { load as parseHTML } from 'cheerio';
import dayjs from 'dayjs';
-export interface IfreedomMetadata {
+export type IfreedomMetadata = {
id: string;
sourceSite: string;
sourceName: string;
filters?: Filters;
-}
+};
class IfreedomPlugin implements Plugin.PluginBase {
id: string;
diff --git a/scripts/multisrc/lightnovelworld/template.ts b/scripts/multisrc/lightnovelworld/template.ts
index d8669be..df857d5 100644
--- a/scripts/multisrc/lightnovelworld/template.ts
+++ b/scripts/multisrc/lightnovelworld/template.ts
@@ -4,18 +4,18 @@ import { Plugin } from '@typings/plugin';
import { Filters, FilterTypes } from '@libs/filterInputs';
import dayjs from 'dayjs';
-interface LightNovelWorldOptions {
+type LightNovelWorldOptions = {
lang?: string;
versionIncrements?: number;
-}
+};
-export interface LightNovelWorldMetadata {
+export type LightNovelWorldMetadata = {
id: string;
sourceSite: string;
sourceName: string;
options?: LightNovelWorldOptions;
filters?: any;
-}
+};
class LightNovelWorld implements Plugin.PagePlugin {
id: string;
diff --git a/scripts/multisrc/lightnovelwp/template.ts b/scripts/multisrc/lightnovelwp/template.ts
index f9301b0..cdbc737 100644
--- a/scripts/multisrc/lightnovelwp/template.ts
+++ b/scripts/multisrc/lightnovelwp/template.ts
@@ -6,21 +6,21 @@ import { NovelStatus } from '@libs/novelStatus';
import { defaultCover } from '@libs/defaultCover';
import { Filters } from '@libs/filterInputs';
-interface LightNovelWPOptions {
+type LightNovelWPOptions = {
reverseChapters?: boolean;
lang?: string;
versionIncrements?: number;
seriesPath?: string;
customJs?: string;
-}
+};
-export interface LightNovelWPMetadata {
+export type LightNovelWPMetadata = {
id: string;
sourceSite: string;
sourceName: string;
options?: LightNovelWPOptions;
filters?: any;
-}
+};
class LightNovelWPPlugin implements Plugin.PluginBase {
id: string;
diff --git a/scripts/multisrc/madara/template.ts b/scripts/multisrc/madara/template.ts
index 6d8096e..8371319 100644
--- a/scripts/multisrc/madara/template.ts
+++ b/scripts/multisrc/madara/template.ts
@@ -9,20 +9,20 @@ import dayjs from 'dayjs';
const includesAny = (str: string, keywords: string[]) =>
new RegExp(keywords.join('|')).test(str);
-interface MadaraOptions {
+type MadaraOptions = {
useNewChapterEndpoint?: boolean;
lang?: string;
orderBy?: string;
versionIncrements?: number;
-}
+};
-export interface MadaraMetadata {
+export type MadaraMetadata = {
id: string;
sourceSite: string;
sourceName: string;
options?: MadaraOptions;
filters?: any;
-}
+};
class MadaraPlugin implements Plugin.PluginBase {
id: string;
diff --git a/scripts/multisrc/mtlnovel/template.ts b/scripts/multisrc/mtlnovel/template.ts
index f2bc2a4..7ff3149 100644
--- a/scripts/multisrc/mtlnovel/template.ts
+++ b/scripts/multisrc/mtlnovel/template.ts
@@ -5,17 +5,17 @@ import { NovelStatus } from '@libs/novelStatus';
import { defaultCover } from '@libs/defaultCover';
import { Filters } from '@libs/filterInputs';
-interface MTLNovelOptions {
+type MTLNovelOptions = {
lang?: string;
-}
+};
-export interface MTLNovelMetadata {
+export type MTLNovelMetadata = {
id: string;
sourceSite: string;
sourceName: string;
options?: MTLNovelOptions;
filters?: any;
-}
+};
class MTLNovelPlugin implements Plugin.PluginBase {
id: string;
@@ -197,11 +197,11 @@ class MTLNovelPlugin implements Plugin.PluginBase {
const result = await res.json();
const novels: Plugin.NovelItem[] = [];
- interface SearchEntry {
+ type SearchEntry = {
title: string;
thumbnail: string;
permalink: string;
- }
+ };
result.items[0].results.map((item: SearchEntry) => {
const novelName = item.title.replace(/<\/?strong>/g, '');
const novelCover = item.thumbnail;
diff --git a/scripts/multisrc/readnovelfull/template.ts b/scripts/multisrc/readnovelfull/template.ts
index f867895..b0eadb0 100644
--- a/scripts/multisrc/readnovelfull/template.ts
+++ b/scripts/multisrc/readnovelfull/template.ts
@@ -3,18 +3,18 @@ import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { NovelStatus } from '@libs/novelStatus';
-interface ReadNovelFullOptions {
+type ReadNovelFullOptions = {
lang?: string;
versionIncrements?: number;
-}
+};
-export interface ReadNovelFullMetadata {
+export type ReadNovelFullMetadata = {
id: string;
sourceSite: string;
sourceName: string;
options?: ReadNovelFullOptions;
filters?: any;
-}
+};
class ReadNovelFullPlugin implements Plugin.PluginBase {
id: string;
diff --git a/scripts/multisrc/readwn/template.ts b/scripts/multisrc/readwn/template.ts
index 348a692..336ffd1 100644
--- a/scripts/multisrc/readwn/template.ts
+++ b/scripts/multisrc/readwn/template.ts
@@ -6,17 +6,17 @@ import { load as parseHTML } from 'cheerio';
import dayjs from 'dayjs';
import qs from 'qs';
-interface ReadwnOptions {
+type ReadwnOptions = {
versionIncrements?: number;
-}
+};
-export interface ReadwnMetadata {
+export type ReadwnMetadata = {
id: string;
sourceSite: string;
sourceName: string;
filters?: any;
options?: ReadwnOptions;
-}
+};
class ReadwnPlugin implements Plugin.PluginBase {
id: string;
diff --git a/scripts/multisrc/rulate/template.ts b/scripts/multisrc/rulate/template.ts
index 93d0b88..58047f1 100644
--- a/scripts/multisrc/rulate/template.ts
+++ b/scripts/multisrc/rulate/template.ts
@@ -5,12 +5,12 @@ import { NovelStatus } from '@libs/novelStatus';
import { load as parseHTML } from 'cheerio';
import dayjs from 'dayjs';
-export interface RulateMetadata {
+export type RulateMetadata = {
id: string;
sourceSite: string;
sourceName: string;
filters?: Filters;
-}
+};
class RulatePlugin implements Plugin.PluginBase {
id: string;
@@ -241,10 +241,10 @@ class RulatePlugin implements Plugin.PluginBase {
};
}
-interface response {
+type response = {
id: number;
title_one: string;
title_two: string;
url: string;
img: string;
-}
+};
diff --git a/src/App.tsx b/src/App.tsx
index ac32fee..21271d9 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -8,8 +8,6 @@ import {
ThemeProvider,
} from '@mui/material';
import Appbar from '@components/Appbar';
-import { Provider } from 'react-redux';
-import store from '@redux/store';
import AppProvider from './provider';
import PopularNovels from '@components/accordions/PopularNovels';
import SearchNovels from '@components/accordions/SearchNovels';
@@ -30,27 +28,25 @@ function App() {
},
});
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/src/components/Appbar/index.tsx b/src/components/Appbar/index.tsx
index 1ff5808..481e12d 100644
--- a/src/components/Appbar/index.tsx
+++ b/src/components/Appbar/index.tsx
@@ -18,42 +18,29 @@ import { Search, Translate } from '@mui/icons-material';
import { searchPlugins } from '@provider/plugins';
import { Plugin } from '@typings/plugin';
import { FixedSizeList, ListChildComponentProps } from 'react-window';
-import { useDispatch } from 'react-redux';
-import { selectPlugin } from '@redux/pluginSlice';
-import usePlugin from '@hooks/usePlugin';
+import { useAppStore } from '@store';
const resovleIcon = (iconPath: string) => {
return '/static/' + iconPath;
};
-export default function () {
- const [searchedPlugins, setSearchPlugins] = useState([]);
- 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) {
+function renderPlugin(setListVisible: (v: boolean) => void) {
+ return function (props: ListChildComponentProps) {
const { index, style, data } = props;
const plugin = data[index];
+ const selectPlugin = useAppStore(state => state.selectPlugin);
return (
{
- dispatch(
- selectPlugin({
- id: plugin.id,
- name: plugin.name,
- version: plugin.version,
- icon: plugin.icon,
- site: plugin.site,
- }),
- );
+ selectPlugin({
+ id: plugin.id,
+ name: plugin.name,
+ version: plugin.version,
+ icon: plugin.icon,
+ site: plugin.site,
+ });
setListVisible(false);
}}
>
@@ -64,7 +51,20 @@ export default function () {
);
- }
+ };
+}
+
+export default function Appbar() {
+ const [searchedPlugins, setSearchPlugins] = useState([]);
+ const [keyword, setKeyword] = useState('');
+ const [listVisible, setListVisible] = useState(false);
+
+ const plugin = useAppStore(state => state.plugin);
+
+ useEffect(() => {
+ const plugins = searchPlugins(keyword);
+ setSearchPlugins(plugins);
+ }, [keyword]);
return (
@@ -111,7 +111,7 @@ export default function () {
backgroundColor: 'white',
}}
>
- {renderPlugin}
+ {renderPlugin(setListVisible)}
) : null}
diff --git a/src/components/PathItem.tsx b/src/components/PathItem.tsx
index 662243f..da5aa88 100644
--- a/src/components/PathItem.tsx
+++ b/src/components/PathItem.tsx
@@ -3,10 +3,10 @@ import { createTheme, ThemeProvider } from '@mui/material/styles';
import React from 'react';
import CopiableItem from './CopiableItem';
-interface Validation {
+type Validation = {
type?: 'error' | 'warning';
message?: string;
-}
+};
const validatePath = (path: string): Validation => {
if (path.includes('http'))
diff --git a/src/components/StyledSwitch.tsx b/src/components/StyledSwitch.tsx
new file mode 100644
index 0000000..2611e0f
--- /dev/null
+++ b/src/components/StyledSwitch.tsx
@@ -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,
+ },
+}));
diff --git a/src/components/accordions/ParseChapter/index.tsx b/src/components/accordions/ParseChapter/index.tsx
index ca9e0b5..7ce0bc9 100644
--- a/src/components/accordions/ParseChapter/index.tsx
+++ b/src/components/accordions/ParseChapter/index.tsx
@@ -1,10 +1,10 @@
import { Box, Button, Stack, TextField } from '@mui/material';
import React, { useState } from 'react';
import AccordionContainer from '../components/AccordionContainer';
-import usePlugin from '@hooks/usePlugin';
import Textarea from '@components/Textarea';
+import { useAppStore } from '@store';
export default function ParseChapter() {
- const plugin = usePlugin();
+ const plugin = useAppStore(state => state.plugin);
const [loading, setLoading] = useState(false);
const [chapterPath, setchapterPath] = useState('');
const [chapterText, setChapterText] = useState('');
diff --git a/src/components/accordions/ParseNovel/index.tsx b/src/components/accordions/ParseNovel/index.tsx
index 8b004c6..6ba0f32 100644
--- a/src/components/accordions/ParseNovel/index.tsx
+++ b/src/components/accordions/ParseNovel/index.tsx
@@ -2,12 +2,12 @@ import { Box, Button, Pagination, Stack, TextField } from '@mui/material';
import React, { useState } from 'react';
import AccordionContainer from '../components/AccordionContainer';
import { Plugin } from '@typings/plugin';
-import usePlugin from '@hooks/usePlugin';
import SourceNovelCard from './SourceNovelCard';
import ChapterList from './ChaptertList';
+import { useAppStore } from '@store';
export default function ParseNovel() {
- const plugin = usePlugin();
+ const plugin = useAppStore(state => state.plugin);
const [sourceNovel, setSouceNovel] = useState<
Plugin.SourceNovel | undefined
>();
diff --git a/src/components/accordions/PopularNovels/index.css b/src/components/accordions/PopularNovels/index.css
new file mode 100644
index 0000000..9aab880
--- /dev/null
+++ b/src/components/accordions/PopularNovels/index.css
@@ -0,0 +1,11 @@
+#filtersContent {
+ display: flex;
+ flex-direction: column;
+ align-content: center;
+}
+
+.switch_filter {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
diff --git a/src/components/accordions/PopularNovels/index.tsx b/src/components/accordions/PopularNovels/index.tsx
index ecf5698..52e1db7 100644
--- a/src/components/accordions/PopularNovels/index.tsx
+++ b/src/components/accordions/PopularNovels/index.tsx
@@ -8,40 +8,42 @@ import {
FormControlLabel,
FormGroup,
} from '@mui/material';
-import React, { useEffect, useState, ChangeEvent } from 'react';
+import React, { useEffect, useState, ChangeEvent, useRef } from 'react';
import AccordionContainer from '../components/AccordionContainer';
import { Plugin } from '@typings/plugin';
import NovelItemCard from '../components/NovelItemCard';
-import { Filters } from '@libs/filterInputs';
-import usePlugin from '@hooks/usePlugin';
+import { Filters, FilterToValues } from '@libs/filterInputs';
+import { useAppStore } from '@store';
+import './index.css';
+import { DialogRef, FiltersDialog } from '../components/filters/FiltersDialog';
export default function PopularNovels() {
- const plugin = usePlugin();
+ const plugin = useAppStore(state => state.plugin);
const [novels, setNovels] = useState([]);
- const [filterValues, setFilterValues] = useState();
+ const [filterValues, setFilterValues] = useState<
+ FilterToValues | undefined
+ >();
const [loading, setLoading] = useState(false);
const [currentIndex, setCurrentIndex] = useState(0);
const [maxIndex, setMaxIndex] = useState(0);
const [isLatest, setIsLatest] = useState(true);
+ const dialogRef = useRef(null);
- const fetchNovelsByIndex = (index: number) => {
+ const fetchNovelsByIndex = async (index: number) => {
if (plugin && index) {
setLoading(true);
- plugin
- .popularNovels(index, {
- filters: filterValues,
- showLatestNovels: isLatest,
- })
- .then(res => {
- if (res.length !== 0) {
- setCurrentIndex(index);
- if (index > maxIndex) {
- setMaxIndex(index);
- }
- setNovels(res);
- }
- })
- .finally(() => setLoading(false));
+ const novels = await plugin.popularNovels(index, {
+ filters: filterValues || {},
+ showLatestNovels: isLatest,
+ });
+ if (novels.length !== 0) {
+ setCurrentIndex(index);
+ if (index > maxIndex) {
+ setMaxIndex(index);
+ }
+ setNovels(novels);
+ }
+ setLoading(false);
}
};
@@ -59,14 +61,15 @@ export default function PopularNovels() {
useEffect(() => {
// Reset when changing plugins.
+ // TODO: Fix crashing on plugin change!
setCurrentIndex(0);
setMaxIndex(0);
setNovels([]);
if (plugin?.filters) {
- const filters = {};
+ const filters: FilterToValues = {};
for (const fKey in plugin.filters) {
- filters[fKey] = {
+ filters[fKey as keyof typeof filters] = {
type: plugin.filters[fKey].type,
value: plugin.filters[fKey].value,
};
@@ -74,6 +77,7 @@ export default function PopularNovels() {
setFilterValues(filters);
}
}, [plugin]);
+
return (
@@ -103,10 +107,25 @@ export default function PopularNovels() {
>
Next Page
+
+ fetchNovelsByIndex(1)}
+ />
fetchNovelsByIndex(value)}
+ onChange={(_event, value) => fetchNovelsByIndex(value)}
>
{Array.from({ length: maxIndex }, (_, index) => index + 1).map(
number => (
diff --git a/src/components/accordions/SearchNovels/index.tsx b/src/components/accordions/SearchNovels/index.tsx
index 8805fdf..4760c68 100644
--- a/src/components/accordions/SearchNovels/index.tsx
+++ b/src/components/accordions/SearchNovels/index.tsx
@@ -3,35 +3,34 @@ import React, { useEffect, useState } from 'react';
import AccordionContainer from '../components/AccordionContainer';
import { Plugin } from '@typings/plugin';
import NovelItemCard from '../components/NovelItemCard';
-import { useSelector } from 'react-redux';
-import { AppState } from '@redux/store';
-import { getPlugin } from '@provider/plugins';
+import { useAppStore } from '@store';
export default function SearchNovels() {
- const selectedPluginItem = useSelector(
- (state: AppState) => state.plugin.selected,
- );
+ const plugin = useAppStore(state => state.plugin);
+
const [searchTerm, setSearchTerm] = useState('');
const [page] = useState(1);
- const [plugin, setPlugin] = useState();
const [novels, setNovels] = useState([]);
const [loading, setLoading] = useState(false);
- useEffect(() => {
- if (selectedPluginItem) {
- setPlugin(getPlugin(selectedPluginItem.id));
- }
- }, [selectedPluginItem]);
+
+ const [fetchError, setFetchError] = useState('');
+
const fetchNovels = () => {
if (plugin) {
setLoading(true);
plugin
.searchNovels(searchTerm, page)
.then(res => setNovels(res))
- .finally(() => setLoading(false));
+ .finally(() => setLoading(false))
+ .catch(e => {
+ setFetchError(e.message);
+ });
}
};
+
return (
+ {fetchError && {fetchError}}
};
+ value: ValueOfFilter;
+ set: (v: typeof value) => void;
+}) => {
+ const label = filter.label.replace(/s+/gi, '-');
+ return (
+
+ {filter.label}
+
+ {filter.options.map(option => {
+ return (
+
+
+ {
+ set(
+ value.includes(option.value)
+ ? value.filter(q => q !== option.value)
+ : [...value, option.value],
+ );
+ }}
+ checked={value.includes(option.value)}
+ />
+ }
+ label={option.label}
+ />
+
+
+ );
+ })}
+
+
+ );
+};
diff --git a/src/components/accordions/components/filters/FiltersDialog.tsx b/src/components/accordions/components/filters/FiltersDialog.tsx
new file mode 100644
index 0000000..a82f1a8
--- /dev/null
+++ b/src/components/accordions/components/filters/FiltersDialog.tsx
@@ -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 | undefined,
+ set: (key: string, v: AnyFilterValue) => void,
+): React.ReactNode => {
+ const isValueCorrectType = (
+ 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(value, filter.value)) {
+ console.error(
+ `FilterValue for filter [${key}] has a wrong type!`,
+ );
+ return null;
+ }
+ // value; // here value has only Picker's value
+ return (
+ 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(value, filter.value)) {
+ console.error(
+ `FilterValue for filter [${key}] has a wrong type!`,
+ );
+ return null;
+ }
+ // value; // here value has only Picker's value
+ return (
+ 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 (
+ 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 (
+ 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 (
+ set(key, newValue)}
+ value={value}
+ key={`checkbox_filter_${key}`}
+ />
+ );
+ }
+ }
+ })}
+ >
+ );
+};
+
+export type DialogRef = { show(): void; hide(): void };
+
+export const FiltersDialog = forwardRef<
+ DialogRef,
+ {
+ values: FilterToValues | undefined;
+ filters: Filters | undefined;
+ setValues: React.Dispatch<
+ React.SetStateAction | undefined>
+ >;
+ refetch(): void;
+ }
+>(function FiltersDialog({ values, setValues, filters, refetch }, ref) {
+ const [dialogOpen, setDialogOpen] = useState(false);
+ const [filterElements, setFilterElements] = useState(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 (
+
+ );
+});
diff --git a/src/components/accordions/components/filters/PickerFilter.tsx b/src/components/accordions/components/filters/PickerFilter.tsx
new file mode 100644
index 0000000..23e18db
--- /dev/null
+++ b/src/components/accordions/components/filters/PickerFilter.tsx
@@ -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 };
+ value: ValueOfFilter;
+ set: (v: ValueOfFilter) => void;
+}) => {
+ const label = filter.label.replace(/s+/gi, '-');
+ return (
+
+
+ {filter.label}
+
+
+
+ );
+};
diff --git a/src/components/accordions/components/filters/SwitchFilter.tsx b/src/components/accordions/components/filters/SwitchFilter.tsx
new file mode 100644
index 0000000..2f0c859
--- /dev/null
+++ b/src/components/accordions/components/filters/SwitchFilter.tsx
@@ -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 };
+ value: ValueOfFilter;
+ set: (v: typeof value) => void;
+}) => {
+ const label = filter.label.replace(/s+/gi, '-');
+ return (
+
+ set(!value)} />}
+ />
+
+ );
+};
diff --git a/src/components/accordions/components/filters/TextFilter.tsx b/src/components/accordions/components/filters/TextFilter.tsx
new file mode 100644
index 0000000..c7107a5
--- /dev/null
+++ b/src/components/accordions/components/filters/TextFilter.tsx
@@ -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 };
+ value: ValueOfFilter;
+ set: (v: typeof value) => void;
+}) => {
+ const label = filter.label.replace(/s+/gi, '-');
+ const textRef = useRef(null);
+ return (
+
+ {
+ if (textRef.current) {
+ set(textRef.current.value ?? '');
+ }
+ }}
+ />
+
+ );
+};
diff --git a/src/components/accordions/components/filters/TriStateCheckbox.tsx b/src/components/accordions/components/filters/TriStateCheckbox.tsx
new file mode 100644
index 0000000..fdd639d
--- /dev/null
+++ b/src/components/accordions/components/filters/TriStateCheckbox.tsx
@@ -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 = (
+
+);
+
+const checkmarkPath = (
+
+);
+
+const uncheckedPath = (
+
+);
+
+export type TriStateCheckboxState = 'checked' | 'unchecked' | 'neutral';
+
+export const TriStateCheckbox = function ({
+ value,
+ onChange,
+}: {
+ value?: TriStateCheckboxState;
+ onChange?: (newValue: TriStateCheckboxState) => void;
+}) {
+ const [state, sSt] = useState(value ?? 'neutral');
+ const setState = (v: TriStateCheckboxState) => {
+ onChange?.(v);
+ sSt(value ?? v);
+ };
+ const root = useRef(null);
+
+ const curVal = value ?? state;
+
+ return (
+ <>
+ {
+ switch (curVal) {
+ case 'checked':
+ return setState('unchecked');
+ case 'neutral':
+ return setState('checked');
+ case 'unchecked':
+ return setState('neutral');
+ }
+ }}
+ >
+
+
+ {curVal === 'checked'
+ ? checkmarkPath
+ : curVal === 'unchecked'
+ ? uncheckedPath
+ : emptyPath}
+
+
+ >
+ );
+};
diff --git a/src/components/accordions/components/filters/TriStateSwitch.tsx b/src/components/accordions/components/filters/TriStateSwitch.tsx
new file mode 100644
index 0000000..f8a348c
--- /dev/null
+++ b/src/components/accordions/components/filters/TriStateSwitch.tsx
@@ -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(value ?? 'neutral');
+ const setState = (v: TriStateSwitchState) => {
+ onChange?.(v);
+ sSt(value ?? v);
+ };
+
+ const root = useRef(null);
+ return (
+ <>
+ {
+ 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');
+ }
+ }}
+ >
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/components/accordions/components/filters/XCheckboxFilter.tsx b/src/components/accordions/components/filters/XCheckboxFilter.tsx
new file mode 100644
index 0000000..81f569a
--- /dev/null
+++ b/src/components/accordions/components/filters/XCheckboxFilter.tsx
@@ -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 };
+ value: ValueOfFilter;
+ 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 (
+
+ {filter.label}
+
+ {filter.options.map(option => {
+ return (
+
+
+ handleChange(option.value, c)}
+ />
+ ) : (
+ handleChange(option.value, c)}
+ />
+ )
+ }
+ label={option.label}
+ />
+
+
+ );
+ })}
+
+
+ );
+};
diff --git a/src/hooks/usePlugin.ts b/src/hooks/usePlugin.ts
deleted file mode 100644
index 2caad34..0000000
--- a/src/hooks/usePlugin.ts
+++ /dev/null
@@ -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();
- useEffect(() => {
- if (selectedPluginItem) {
- setPlugin(getPlugin(selectedPluginItem.id));
- }
- }, [selectedPluginItem]);
- return plugin;
-}
diff --git a/src/libs/fetch.ts b/src/libs/fetch.ts
index cd59d5f..401d2b6 100644
--- a/src/libs/fetch.ts
+++ b/src/libs/fetch.ts
@@ -2,7 +2,7 @@
import { parse as parseProto } from 'protobufjs';
-interface FetchInit {
+type FetchInit = {
headers?: Record | Headers;
method?: string;
body?: FormData | string;
@@ -12,7 +12,7 @@ interface FetchInit {
| undefined
| FormData
| Headers;
-}
+};
const makeInit = async (init?: FetchInit) => {
const defaultHeaders: Record = {
@@ -100,13 +100,13 @@ export const fetchText = async function (
}
};
-interface ProtoRequestInit {
+type ProtoRequestInit = {
// merged .proto file
proto: string;
requestType: string;
requestData?: any;
responseType: string;
-}
+};
const BYTE_MARK = BigInt((1 << 8) - 1);
diff --git a/src/libs/filterInputs.ts b/src/libs/filterInputs.ts
index 1b15a9e..8cc7eeb 100644
--- a/src/libs/filterInputs.ts
+++ b/src/libs/filterInputs.ts
@@ -1,8 +1,14 @@
-export interface FilterOption {
+/**
+ * Filter's Options user can choose from
+ */
+export type FilterOption = {
readonly label: string;
readonly value: string;
-}
+};
+/**
+ * Every currently implemented FilterType
+ */
export enum FilterTypes {
TextInput = 'Text',
Picker = 'Picker',
@@ -11,59 +17,83 @@ export enum FilterTypes {
ExcludableCheckboxGroup = 'XCheckbox',
}
-interface SwitchFilter {
+type SwitchFilter = {
type: FilterTypes.Switch;
/** Default value */
value: boolean;
-}
+};
-interface TextFilter {
+type TextFilter = {
type: FilterTypes.TextInput;
/** Default value */
value: string;
-}
+};
-interface CheckboxFilter {
+type CheckboxFilter = {
type: FilterTypes.CheckboxGroup;
options: readonly FilterOption[];
/** Default value */
value: string[];
-}
-interface PickerFilter {
+};
+type PickerFilter = {
type: FilterTypes.Picker;
options: readonly FilterOption[];
/** Default value */
value: string;
-}
+};
-interface ExcludableCheckboxFilter {
+type ExcludableCheckboxFilter = {
type: FilterTypes.ExcludableCheckboxGroup;
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,
- { label: string } & (
- | PickerFilter
- | CheckboxFilter
- | TextFilter
- | SwitchFilter
- | ExcludableCheckboxFilter
- )
->;
+/**
+ * key - filter pairs
+ */
+export type Filters = Record>;
+/** 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 = {
+ label: string;
+} & FilterFromType[Type];
+
+/**
+ * Strip {@link FilterObject} object from 'label' and 'options' to get key - filter_value pairs
+ * @see {@link ValueOfFilter}
+ */
export type FilterToValues<
- T extends Record | undefined,
-> = T extends undefined
+ FilterObject extends Record | undefined,
+> = FilterObject extends undefined
? undefined
: {
- [K in keyof T]: Omit<
- { type: NonNullable[K]['type'] } & Filters[string],
- 'label' | 'options'
+ // copy the Filters object, but just get {value,type} pairs instead of the whole Filter object
+ [SingleFilter in keyof FilterObject]: FilterValueWithType<
+ FilterType[SingleFilter]>
>;
};
+/**
+ * Get value type for a {@link Filter} given it's FilterType
+ * @see {@link FilterTypes}
+ */
export type ValueOfFilter =
T extends FilterTypes.CheckboxGroup
? CheckboxFilter['value']
@@ -76,3 +106,25 @@ export type ValueOfFilter =
: T extends FilterTypes.ExcludableCheckboxGroup
? ExcludableCheckboxFilter['value']
: never;
+
+/** Get {@link Filter}'s type */
+export type FilterType = T extends {
+ type: infer K;
+}
+ ? K extends FilterTypes
+ ? K
+ : never
+ : never;
+
+/** Get {type, value} types for given FilterType
+ * @see {@link ValueOfFilter}
+ */
+export type FilterValueWithType = {
+ type: T;
+ value: ValueOfFilter;
+};
+
+/**
+ * Any possible filter value
+ */
+export type AnyFilterValue = ValueOfFilter;
diff --git a/src/plugins/english/lnmtl.ts b/src/plugins/english/lnmtl.ts
index 76e847e..854caff 100644
--- a/src/plugins/english/lnmtl.ts
+++ b/src/plugins/english/lnmtl.ts
@@ -367,14 +367,14 @@ class LnMTLPlugin implements Plugin.PagePlugin {
export default new LnMTLPlugin();
-interface ChapterEntry {
+type ChapterEntry = {
number: number;
title: string;
slug: string;
created_at: string;
-}
+};
-interface VolumeEntry {
+type VolumeEntry = {
id: string;
title: string;
-}
+};
diff --git a/src/plugins/english/ranobes.ts b/src/plugins/english/ranobes.ts
index 89dbb57..4d21fe9 100644
--- a/src/plugins/english/ranobes.ts
+++ b/src/plugins/english/ranobes.ts
@@ -369,9 +369,9 @@ class RanobesPlugin implements Plugin.PagePlugin {
}
export default new RanobesPlugin();
-interface ChapterEntry {
+type ChapterEntry = {
id: number;
title: string;
date: string;
link: string;
-}
+};
diff --git a/src/plugins/english/relibrary.ts b/src/plugins/english/relibrary.ts
index cc30b86..7e8376f 100644
--- a/src/plugins/english/relibrary.ts
+++ b/src/plugins/english/relibrary.ts
@@ -6,10 +6,10 @@ import { defaultCover } from '@libs/defaultCover';
import { NovelItem } from '../../test_web/static/js';
// import { isUrlAbsolute } from "@libs/isAbsoluteUrl";
-interface FuzzySearchOptions {
+type FuzzySearchOptions = {
caseSensitive: boolean;
sort: boolean;
-}
+};
// 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.
diff --git a/src/plugins/english/royalroad.ts b/src/plugins/english/royalroad.ts
index 26ac8b9..b0f0c1d 100644
--- a/src/plugins/english/royalroad.ts
+++ b/src/plugins/english/royalroad.ts
@@ -722,18 +722,18 @@ class RoyalRoad implements Plugin.PluginBase {
export default new RoyalRoad();
-interface ChapterEntry {
+type ChapterEntry = {
id: number;
volumeId: number;
title: string;
date: string;
order: number;
url: string;
-}
+};
-interface VolumeEntry {
+type VolumeEntry = {
id: number;
title: string;
cover: string;
order: number;
-}
+};
diff --git a/src/plugins/english/wtrlab.ts b/src/plugins/english/wtrlab.ts
index 98e9f6d..eb6801a 100644
--- a/src/plugins/english/wtrlab.ts
+++ b/src/plugins/english/wtrlab.ts
@@ -205,22 +205,22 @@ class WTRLAB implements Plugin.PluginBase {
} satisfies Filters;
}
-interface NovelJson {
+type NovelJson = {
props: Props;
page: string;
-}
+};
-interface Props {
+type Props = {
pageProps: PageProps;
__N_SSP: boolean;
-}
+};
-interface PageProps {
+type PageProps = {
serie: Serie;
server_time: Date;
-}
+};
-interface Serie {
+type Serie = {
serie_data: SerieData;
chapters: Chapter[];
recommendation: SerieData[];
@@ -231,9 +231,9 @@ interface Serie {
data: Data;
is_default: boolean;
raw_type: string;
-}
+};
-interface Chapter {
+type Chapter = {
serie_id: number;
id: number;
order: number;
@@ -242,16 +242,16 @@ interface Chapter {
name: string;
created_at: string;
updated_at: string;
-}
-interface ChapterData {
+};
+type ChapterData = {
data: ChapterContent;
-}
-interface ChapterContent {
+};
+type ChapterContent = {
title: string;
body: string;
-}
+};
-interface SerieData {
+type SerieData = {
serie_id?: number;
recommendation_id?: number;
score?: string;
@@ -273,26 +273,26 @@ interface SerieData {
from: null;
raw_id: number;
genres?: number[];
-}
+};
-interface Data {
+type Data = {
title: string;
author: string;
description: string;
image: string;
-}
+};
-interface JsonNovel {
+type JsonNovel = {
success: boolean;
data: Datum[];
-}
-interface Datum {
+};
+type Datum = {
serie: Serie;
chapters: Chapter[];
updated_at: Date;
raw_id: number;
slug: string;
data: Data;
-}
+};
export default new WTRLAB();
diff --git a/src/plugins/english/wuxiaworld.ts b/src/plugins/english/wuxiaworld.ts
index 5ab3b2c..e3b3356 100644
--- a/src/plugins/english/wuxiaworld.ts
+++ b/src/plugins/english/wuxiaworld.ts
@@ -4,35 +4,35 @@ import { Plugin } from '@typings/plugin';
import { Filters } from '@libs/filterInputs';
import { NovelStatus } from '@libs/novelStatus';
-interface StringValue {
+type StringValue = {
value: string;
-}
+};
-interface BoolValue {
+type BoolValue = {
value: boolean;
-}
+};
-interface DecimalValue {
+type DecimalValue = {
units: bigint;
nanos: number;
-}
+};
-interface Timestamp {
+type Timestamp = {
seconds: number;
nanos: number;
-}
+};
-interface NovelEntry {
+type NovelEntry = {
name: string;
coverUrl: string;
slug: string;
-}
+};
-interface RelatedChapterUserInfo {
+type RelatedChapterUserInfo = {
isChapterUnlocked?: BoolValue;
-}
+};
-interface ChapterItem {
+type ChapterItem = {
entityId: number;
name: string;
slug: string;
@@ -41,19 +41,19 @@ interface ChapterItem {
relatedUserInfo?: RelatedChapterUserInfo;
offset: number;
publishedAt?: Timestamp;
-}
+};
-interface GetChapterResponse {
+type GetChapterResponse = {
item?: ChapterItem;
-}
+};
-interface ChapterGroupCounts {
+type ChapterGroupCounts = {
total: number;
advance: number;
normal: number;
-}
+};
-interface ChapterGroupItem {
+type ChapterGroupItem = {
id: number;
title: string;
order: number;
@@ -61,11 +61,11 @@ interface ChapterGroupItem {
toChapterNumber?: DecimalValue;
chapterList: ChapterItem[];
counts?: ChapterGroupCounts;
-}
+};
-interface GetChapterListResponse {
+type GetChapterListResponse = {
items: ChapterGroupItem[];
-}
+};
enum NovelItem_Status {
Finished = 0,
@@ -74,14 +74,14 @@ enum NovelItem_Status {
All = -1,
}
-interface NovelKarmaInfo {
+type NovelKarmaInfo = {
isActive: boolean;
isFree: boolean;
maxFreeChapter?: DecimalValue;
canUnlockWithVip: boolean;
-}
+};
-interface NovelItem {
+type NovelItem = {
id: number;
name: string;
slug: string;
@@ -94,11 +94,11 @@ interface NovelItem {
authorName?: StringValue;
karmaInfo?: NovelKarmaInfo;
genres: string[];
-}
+};
-interface GetNovelResponse {
+type GetNovelResponse = {
item?: NovelItem;
-}
+};
class WuxiaWorld implements Plugin.PluginBase {
id = 'wuxiaworld';
diff --git a/src/plugins/index.d.ts b/src/plugins/index.d.ts
index a50fccb..3421420 100644
--- a/src/plugins/index.d.ts
+++ b/src/plugins/index.d.ts
@@ -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;
diff --git a/src/plugins/japanese/Syosetu.ts b/src/plugins/japanese/Syosetu.ts
index 069c800..5827c35 100644
--- a/src/plugins/japanese/Syosetu.ts
+++ b/src/plugins/japanese/Syosetu.ts
@@ -3,7 +3,6 @@ import { fetchApi } from '@libs/fetch';
import { Plugin } from '@typings/plugin';
import { defaultCover } from '@libs/defaultCover';
import { FilterTypes, Filters } from '@libs/filterInputs';
-import { NovelItem } from '../../test_web/static/js';
// const novelStatus = require('@libs/novelStatus');
// const isUrlAbsolute = require('@libs/isAbsoluteUrl');
// const parseDate = require('@libs/parseDate');
@@ -52,13 +51,13 @@ class Syosetu implements Plugin.PluginBase {
if (parseInt(loadedCheerio('.is-current').html() || '1') !== pagenumber)
return [];
- const novels: NovelItem[] = [];
+ const novels: Plugin.NovelItem[] = [];
loadedCheerio('.c-card').each((_, e) => {
const anchor = loadedCheerio(e).find('.p-ranklist-item__title a');
const url = anchor.attr('href');
if (!url) return;
const name = anchor.text();
- const novel: NovelItem = {
+ const novel: Plugin.NovelItem = {
path: url.replace(this.novelPrefix, ''),
name,
cover: defaultCover,
@@ -93,12 +92,12 @@ class Syosetu implements Plugin.PluginBase {
novel.summary = loadedCheerio('#novel_ex')
.text()
.replace(/<\s*br.*?>/g, '\n');
- cqGetChapters.each(() => {
- const chapterA = loadedCheerio(this).find('a');
+ cqGetChapters.each((_, e) => {
+ const chapterA = loadedCheerio(e).find('a');
const [chapterName, releaseDate, chapterUrl] = [
// set the variables
chapterA.text(),
- loadedCheerio(this)
+ loadedCheerio(e)
.find('dt') // get title
.text() // get text
.replace(/(.)/g, '') // remove "(edited)" mark
diff --git a/src/plugins/korean/Agitoon.ts b/src/plugins/korean/Agitoon.ts
index cb08b3e..5a4650a 100644
--- a/src/plugins/korean/Agitoon.ts
+++ b/src/plugins/korean/Agitoon.ts
@@ -181,12 +181,12 @@ class Agitoon implements Plugin.PluginBase {
export default new Agitoon();
-interface response {
+type response = {
list_limit: number;
list?: ListEntity[] | null;
list_count: number;
-}
-interface ListEntity {
+};
+type ListEntity = {
wr_id: string;
wr_subject: string;
np_dir: string;
@@ -200,13 +200,13 @@ interface ListEntity {
np_country: string;
np_age: string;
is_scrap: number;
-}
+};
-interface responseBook {
+type responseBook = {
list?: ListEntity2[] | null;
download_time: string;
-}
-interface ListEntity2 {
+};
+type ListEntity2 = {
wr_id: string;
wr_subject: string;
wr_datetime: string;
@@ -214,4 +214,4 @@ interface ListEntity2 {
novel_c_style1: string;
novel_c_str1: string;
data_novel_c_view: string;
-}
+};
diff --git a/src/plugins/russian/authortoday.ts b/src/plugins/russian/authortoday.ts
index 9f6bf5e..35d83e6 100644
--- a/src/plugins/russian/authortoday.ts
+++ b/src/plugins/russian/authortoday.ts
@@ -368,14 +368,14 @@ function decrypt(
return text;
}
-interface authorization {
+type authorization = {
userId: number;
token: string;
issued: string;
expires: string;
-}
+};
-interface currentUser {
+type currentUser = {
id: number;
userName: string;
fio: string;
@@ -391,9 +391,9 @@ interface currentUser {
emailConfirmed: boolean;
isDeleted: boolean;
isDisabled: boolean;
-}
+};
-interface response {
+type response = {
searchResults?: SearchResultsEntity[] | null;
realTotalCount: number;
code?: string;
@@ -402,8 +402,8 @@ interface response {
format: Format;
duration: Duration;
isWorkMarkEnabled: boolean;
-}
-interface SearchResultsEntity {
+};
+type SearchResultsEntity = {
id: number;
title: string;
annotation: string;
@@ -446,20 +446,20 @@ interface SearchResultsEntity {
isLiked: boolean;
isWorkMarkEnabled: boolean;
workInLibraryState: string;
-}
-interface Format {
+};
+type Format = {
enumValue: string;
value: string;
title: string;
mobileTitle: string;
-}
-interface Duration {
+};
+type Duration = {
value: string;
title: string;
mobileTitle: string;
-}
+};
-interface responseBook {
+type responseBook = {
message?: string;
chapters?: ChaptersEntity[] | null;
allowDownloads: boolean;
@@ -543,8 +543,8 @@ interface responseBook {
purchaseTime?: null;
likeTime?: null;
markTime?: null;
-}
-interface ChaptersEntity {
+};
+type ChaptersEntity = {
id: number;
workId: number;
title: string;
@@ -554,16 +554,16 @@ interface ChaptersEntity {
lastModificationTime: string;
textLength: number;
isAvailable: boolean;
-}
+};
-interface GalleryImagesEntity {
+type GalleryImagesEntity = {
id: string;
caption?: null;
url: string;
height: number;
width: number;
-}
-interface RecommendationsEntity {
+};
+type RecommendationsEntity = {
title: string;
coverUrl: string;
authorId: number;
@@ -590,9 +590,9 @@ interface RecommendationsEntity {
removalReason?: null;
removalReasonComment?: null;
id: number;
-}
+};
-interface encryptedСhapter {
+type encryptedСhapter = {
id: number;
title: string;
isDraft: boolean;
@@ -602,4 +602,4 @@ interface encryptedСhapter {
key: string;
code?: string;
message?: string;
-}
+};
diff --git a/src/plugins/russian/bookriver.ts b/src/plugins/russian/bookriver.ts
index e5afcdf..9f53c31 100644
--- a/src/plugins/russian/bookriver.ts
+++ b/src/plugins/russian/bookriver.ts
@@ -226,7 +226,7 @@ class Bookriver implements Plugin.PluginBase {
}
export default new Bookriver();
-interface response {
+type response = {
props: {
pageProps: {
state: {
@@ -241,9 +241,9 @@ interface response {
};
};
};
-}
+};
-interface BooksEntity {
+type BooksEntity = {
name: string;
coverImages: { url: string }[];
slug: string;
@@ -256,17 +256,17 @@ interface BooksEntity {
chapters: Chapter[];
};
statusComplete: string;
-}
+};
-interface Chapter {
+type Chapter = {
name: string;
available: boolean;
firstPublishedAt?: Date | null;
createdAt?: Date | null;
chapterId: number | string;
-}
+};
-interface responseChapter {
+type responseChapter = {
data: {
id: number;
bookId: number;
@@ -288,12 +288,12 @@ interface responseChapter {
};
publicationScheduledFor?: null;
};
-}
+};
-interface responseSearch {
+type responseSearch = {
data: Data;
total: number;
-}
-interface Data {
+};
+type Data = {
books?: BooksEntity[] | null;
-}
+};
diff --git a/src/plugins/russian/ficbook.ts b/src/plugins/russian/ficbook.ts
index e433807..d04bdf9 100644
--- a/src/plugins/russian/ficbook.ts
+++ b/src/plugins/russian/ficbook.ts
@@ -2650,11 +2650,11 @@ class ficbook implements Plugin.PluginBase {
export default new ficbook();
-interface Data {
+type Data = {
data?: DataEntity[] | null;
more: boolean;
-}
-interface DataEntity {
+};
+type DataEntity = {
id: string;
slug: string;
author_id: number;
@@ -2673,4 +2673,4 @@ interface DataEntity {
readedDate?: string | null;
reward_cnt: number;
cover?: string | null;
-}
+};
diff --git a/src/plugins/russian/neobook.ts b/src/plugins/russian/neobook.ts
index ad71a27..8d9068c 100644
--- a/src/plugins/russian/neobook.ts
+++ b/src/plugins/russian/neobook.ts
@@ -213,13 +213,13 @@ class Neobook implements Plugin.PluginBase {
export default new Neobook();
-interface BundleBooks {
+type BundleBooks = {
categories: any[];
topSection: any[];
feed: Novels[];
-}
+};
-interface BottomSection {
+type BottomSection = {
id: string;
boost: string;
preferenceid: string;
@@ -230,9 +230,9 @@ interface BottomSection {
more: string;
part: string;
items: Novels[];
-}
+};
-interface Novels {
+type Novels = {
status?: string;
error?: string;
login?: Login;
@@ -290,9 +290,9 @@ interface Novels {
carouselItems?: Novels[];
book?: Novels;
activeChapterIndex?: number;
-}
+};
-interface Attachment {
+type Attachment = {
has: string;
id: string;
typeid: string;
@@ -304,16 +304,16 @@ interface Attachment {
video: string;
origin: string;
ext: string;
-}
+};
-interface Image {
+type Image = {
s: string;
m: string;
l: string;
has?: string;
-}
+};
-interface Chapter {
+type Chapter = {
id: string;
token: string;
status: string;
@@ -324,9 +324,9 @@ interface Chapter {
access: string;
loaded: string;
data?: Data;
-}
+};
-interface Data {
+type Data = {
available: string;
access: string;
id: string;
@@ -339,27 +339,27 @@ interface Data {
datePublish: string;
attachment: Attachment;
lastComments: any[];
-}
+};
-interface NovelsCounters {
+type NovelsCounters = {
impressions: Bookmarks;
views: Bookmarks;
likes: Bookmarks;
comments: Bookmarks;
bookmarks: Bookmarks;
-}
+};
-interface Bookmarks {
+type Bookmarks = {
value: string;
formatted: string;
-}
+};
-interface Feed {
+type Feed = {
name: string;
canRemoved: string;
-}
+};
-interface Login {
+type Login = {
uid: string;
utoken: string;
has: string;
@@ -376,14 +376,14 @@ interface Login {
deposit: any[];
affiliate: any[];
preferences: any[];
-}
+};
-interface LoginCounters {
+type LoginCounters = {
notifications: string;
messages: string;
-}
+};
-interface LoginUser {
+type LoginUser = {
id: string;
username: string;
firstname: string;
@@ -392,39 +392,39 @@ interface LoginUser {
status: string;
balance: Balance;
connectedService: string;
-}
+};
-interface Balance {
+type Balance = {
neo: Price;
-}
+};
-interface Price {
+type Price = {
has: string;
value: string;
currency: Currency;
formatted: string;
deadline: string;
-}
+};
type Currency = 'NEO';
-interface Rating {
+type Rating = {
value: string;
count: string;
my: string;
-}
+};
-interface ReadProgress {
+type ReadProgress = {
chapter: string;
progress: string;
-}
+};
-interface ReadTime {
+type ReadTime = {
hours: string;
minutes: string;
-}
+};
-interface NovelsUser {
+type NovelsUser = {
available: string;
boosted: string;
id: string;
@@ -452,4 +452,4 @@ interface NovelsUser {
isSubscription: string;
isBlock: string;
gr: string;
-}
+};
diff --git a/src/plugins/russian/novelOvh.ts b/src/plugins/russian/novelOvh.ts
index 93c4faf..50f08d1 100644
--- a/src/plugins/russian/novelOvh.ts
+++ b/src/plugins/russian/novelOvh.ts
@@ -183,7 +183,7 @@ class novelOvh implements Plugin.PluginBase {
export default new novelOvh();
-interface BooksEntity {
+type BooksEntity = {
id: string;
slug: string;
type: string;
@@ -211,18 +211,18 @@ interface BooksEntity {
chaptersCount: number;
createdAt: string;
updatedAt: string;
-}
-interface Name {
+};
+type Name = {
ru: string;
original: string;
en: string;
-}
-interface AltNamesEntity {
+};
+type AltNamesEntity = {
language: string;
name: string;
-}
+};
-interface responseNovel {
+type responseNovel = {
book: Book;
branches: BranchesEntity[];
chapters: ChaptersEntity[];
@@ -233,8 +233,8 @@ interface responseNovel {
promotionVideos?: null[] | null;
folderItems?: null[] | null;
contentNotices?: null[] | null;
-}
-interface Book {
+};
+type Book = {
id: string;
slug: string;
type: string;
@@ -273,17 +273,17 @@ interface Book {
createdAt: string;
updatedAt: string;
rating?: null;
-}
-interface LabelsEntity {
+};
+type LabelsEntity = {
id: string;
slug: string;
name: string;
-}
-interface RelationsEntity {
+};
+type RelationsEntity = {
type: string;
publisher: Publisher;
-}
-interface Publisher {
+};
+type Publisher = {
id: string;
kind: string;
slug: string;
@@ -298,8 +298,8 @@ interface Publisher {
tonAddress?: null;
createdAt: string;
updatedAt: string;
-}
-interface BranchesEntity {
+};
+type BranchesEntity = {
id: string;
licensed: boolean;
editorsChoice: boolean;
@@ -309,8 +309,8 @@ interface BranchesEntity {
publishers?: PublishersEntity[] | null;
subscription?: null;
bookmark?: null;
-}
-interface PublishersEntity {
+};
+type PublishersEntity = {
id: string;
kind: string;
slug: string;
@@ -324,8 +324,8 @@ interface PublishersEntity {
tonAddress?: null;
createdAt: string;
updatedAt: string;
-}
-interface ChaptersEntity {
+};
+type ChaptersEntity = {
id: string;
name?: string | null;
title?: string | null;
@@ -341,8 +341,8 @@ interface ChaptersEntity {
createdAt: string;
expiredAt: string;
updatedAt: string;
-}
-interface RelatedEntityOrBook {
+};
+type RelatedEntityOrBook = {
id: string;
type: string;
slug: string;
@@ -361,23 +361,23 @@ interface RelatedEntityOrBook {
averageRating: number;
createdAt: string;
updatedAt: string;
-}
-interface AssociatedEntity {
+};
+type AssociatedEntity = {
id: string;
kind: string;
createdAt: string;
updatedAt: string;
book: RelatedEntityOrBook;
-}
-interface CoversEntity {
+};
+type CoversEntity = {
id: string;
volume: number;
image: string;
createdAt: string;
updatedAt: string;
-}
+};
-interface responseChapter {
+type responseChapter = {
id: string;
name?: string;
title: string;
@@ -397,22 +397,22 @@ interface responseChapter {
createdAt: string;
expiredAt: string;
updatedAt: string;
-}
-interface Content {
+};
+type Content = {
type: string;
content?: ContentEntity[] | null;
-}
-interface ContentEntity {
+};
+type ContentEntity = {
text?: string | null;
type: string;
attrs?: Attrs | null;
content?: Content[] | null;
-}
-interface Attrs {
+};
+type Attrs = {
textAlign?: string;
pages?: [string];
-}
-interface PagesEntity {
+};
+type PagesEntity = {
id: string;
image: string;
index: number;
@@ -420,4 +420,4 @@ interface PagesEntity {
height: number;
width: number;
createdAt: string;
-}
+};
diff --git a/src/plugins/russian/novelTL.ts b/src/plugins/russian/novelTL.ts
index b9786a5..0371ad3 100644
--- a/src/plugins/russian/novelTL.ts
+++ b/src/plugins/russian/novelTL.ts
@@ -851,25 +851,25 @@ class TL implements Plugin.PluginBase {
export default new TL();
-interface response {
+type response = {
data: Data;
-}
+};
-interface Data {
+type Data = {
projects?: Projects;
project?: Project;
chapter?: DataChapter;
-}
+};
-interface DataChapter {
+type DataChapter = {
text: Text;
-}
+};
-interface Text {
+type Text = {
text: string;
-}
+};
-interface Project {
+type Project = {
title: string;
translationStatus: string;
fullUrl: string;
@@ -879,58 +879,58 @@ interface Project {
tags: Genre[];
annotation: Text;
subprojects: Subprojects;
-}
+};
-interface Cover {
+type Cover = {
url: string;
-}
+};
-interface Genre {
+type Genre = {
nameRu: string;
nameEng: string;
-}
+};
-interface Person {
+type Person = {
role: string;
name: Name;
-}
+};
-interface Name {
+type Name = {
firstName: string;
lastName: string;
-}
+};
-interface Subprojects {
+type Subprojects = {
content: SubprojectsContent[];
-}
+};
-interface SubprojectsContent {
+type SubprojectsContent = {
title: null | string;
volumes: Volumes;
-}
+};
-interface Volumes {
+type Volumes = {
content: VolumesContent[];
-}
+};
-interface VolumesContent {
+type VolumesContent = {
shortName: null | string;
chapters: ChapterElement[];
-}
+};
-interface ChapterElement {
+type ChapterElement = {
title: null | string;
publishDate: Date;
fullUrl: string;
published: boolean;
-}
+};
-interface Projects {
+type Projects = {
content: ProjectsContent[];
-}
+};
-interface ProjectsContent {
+type ProjectsContent = {
title: string;
fullUrl: string;
covers: Cover[];
-}
+};
diff --git a/src/plugins/russian/ranobehub.ts b/src/plugins/russian/ranobehub.ts
index 4f2a46b..531021c 100644
--- a/src/plugins/russian/ranobehub.ts
+++ b/src/plugins/russian/ranobehub.ts
@@ -1147,7 +1147,7 @@ class RNBH implements Plugin.PluginBase {
export default new RNBH();
-interface responseNovels {
+type responseNovels = {
id: number;
names: Names;
rating: number;
@@ -1158,27 +1158,27 @@ interface responseNovels {
status: string;
user?: User;
counts: Counts;
-}
-interface Names {
+};
+type Names = {
eng?: string;
rus?: string;
original: string;
-}
-interface Poster {
+};
+type Poster = {
medium: string;
small: string;
color: string;
-}
-interface User {
+};
+type User = {
status?: null;
liked: boolean;
-}
-interface Counts {
+};
+type Counts = {
volumes: string;
chapters: string;
-}
+};
-interface responseNovel {
+type responseNovel = {
id: number;
names: Names;
rating: number;
@@ -1195,55 +1195,53 @@ interface responseNovel {
start_reading_url: string;
html: string;
tags: Tags;
-}
-interface Posters {
+};
+type Posters = {
big: string;
medium: string;
small: string;
tiny: string;
color: string;
-}
-interface AuthorsEntity {
+};
+type AuthorsEntity = {
name_eng: string;
pivot: Pivot;
-}
-interface Pivot {
+};
+type Pivot = {
ranobe_id: number;
author_id?: number;
translator_id?: number;
-}
-interface TranslatorsEntity {
+};
+type TranslatorsEntity = {
name: string;
pivot: Pivot;
-}
-interface Status {
+};
+type Status = {
id: number;
title: string;
-}
-interface Tags {
+ name: string;
+};
+type Tags = {
events?: GenresOrEntity[] | null;
genres?: GenresOrEntity[] | null;
-}
-interface GenresOrEntity {
+};
+type GenresOrEntity = {
id: number;
names: Names;
url: string;
title: string;
description?: string | null;
-}
+};
-interface VolumesEntity {
+type VolumesEntity = {
id: number;
num: number;
name: string;
status: Status;
chapters?: ChaptersEntity[] | null;
-}
-interface Status {
- id: number;
- name: string;
-}
-interface ChaptersEntity {
+};
+
+type ChaptersEntity = {
id: number;
name: string;
num: number;
@@ -1252,18 +1250,18 @@ interface ChaptersEntity {
has_images: boolean;
changed_at: string;
comments_count: string;
-}
+};
-interface responseSearch {
+type responseSearch = {
meta: Meta;
collections?: DataEntity[] | null;
data?: DataEntity[] | null;
-}
-interface Meta {
+};
+type Meta = {
key: string;
title: string;
-}
-interface DataEntity {
+};
+type DataEntity = {
id: number;
names?: Names | null;
description?: string | null;
@@ -1275,10 +1273,10 @@ interface DataEntity {
roles?: null[] | null;
avatar?: Avatar | null;
has_plus?: boolean | null;
-}
-interface Avatar {
+};
+type Avatar = {
big: string;
color: string;
thumb: string;
is_default: boolean;
-}
+};
diff --git a/src/plugins/russian/ranobelib.ts b/src/plugins/russian/ranobelib.ts
index df92577..196d7d0 100644
--- a/src/plugins/russian/ranobelib.ts
+++ b/src/plugins/russian/ranobelib.ts
@@ -526,82 +526,82 @@ function jsonToHtml(json: HTML[], images, html = '') {
return html;
}
-interface HTML {
+type HTML = {
type: string;
content?: HTML[];
attrs?: Attrs;
text?: string;
-}
+};
-interface Attrs {
+type Attrs = {
src: string;
alt: string | null;
title: string | null;
-}
+};
-interface authorization {
+type authorization = {
token: Token;
auth: Auth;
timestamp: number;
-}
-interface Token {
+};
+type Token = {
token_type: string;
expires_in: number;
access_token: string;
refresh_token: string;
timestamp: number;
-}
-interface Auth {
+};
+type Auth = {
id: number;
username: string;
avatar: Cover;
last_online_at: string;
metadata: Metadata;
-}
-interface Metadata {
+};
+type Metadata = {
auth_domains: string;
-}
+};
-interface TopLevel {
+type TopLevel = {
data: DataClass | DataClass[];
links?: Links;
meta?: Meta;
-}
+};
-interface AgeRestriction {
+type AgeRestriction = {
id: number;
label: string;
-}
+};
-interface Branch {
+type Branch = {
id: number;
branch_id: null;
created_at: string;
teams: BranchTeam[];
user: User;
-}
+};
-interface BranchTeam {
+type BranchTeam = {
id: number;
slug: string;
slug_url: string;
model: string;
name: string;
cover: Cover;
-}
+};
-interface Cover {
+type Cover = {
filename: null | string;
thumbnail: string;
default: string;
-}
+};
-interface User {
+type User = {
username: string;
id: number;
-}
+};
-interface DataClass {
+type DataClass = {
id: number;
name: string;
rus_name?: string;
@@ -633,9 +633,9 @@ interface DataClass {
likes_count?: number;
content?: any;
attachments?: Attachment[];
-}
+};
-interface Artist {
+type Artist = {
id: number;
slug: string;
slug_url: string;
@@ -648,16 +648,16 @@ interface Artist {
confirmed: null;
user_id: number;
titles_count_details: null;
-}
+};
-interface Subscription {
+type Subscription = {
is_subscribed: boolean;
source_type: string;
source_id: number;
relation: null;
-}
+};
-interface Attachment {
+type Attachment = {
id?: string;
filename: string;
name: string;
@@ -665,14 +665,14 @@ interface Attachment {
url: string;
width: number;
height: number;
-}
+};
-interface Genre {
+type Genre = {
id: number;
name: string;
-}
+};
-interface DataTeam {
+type DataTeam = {
id: number;
slug: string;
slug_url: string;
@@ -682,22 +682,22 @@ interface DataTeam {
details?: Details;
vk?: string;
discord?: null;
-}
+};
-interface Details {
+type Details = {
branch_id: null;
is_active: boolean;
subscriptions_count: null;
-}
+};
-interface Links {
+type Links = {
first: string;
last: null;
prev: null;
next: string;
-}
+};
-interface Meta {
+type Meta = {
current_page?: number;
from?: number;
path?: string;
@@ -707,9 +707,9 @@ interface Meta {
has_next_page?: boolean;
seed?: string;
country?: string;
-}
+};
-interface DataChapter {
+type DataChapter = {
id: number;
index: number;
item_number: number;
@@ -719,4 +719,4 @@ interface DataChapter {
name: string;
branches_count: number;
branches: Branch[];
-}
+};
diff --git a/src/plugins/russian/ranoberf.ts b/src/plugins/russian/ranoberf.ts
index f8e7b69..bbac441 100644
--- a/src/plugins/russian/ranoberf.ts
+++ b/src/plugins/russian/ranoberf.ts
@@ -143,7 +143,7 @@ class RNRF implements Plugin.PluginBase {
export default new RNRF();
-interface response {
+type response = {
props: Props;
page: string;
query: Query;
@@ -152,25 +152,25 @@ interface response {
gssp: boolean;
appGip: boolean;
dynamicIds?: string[];
-}
+};
-interface Props {
+type Props = {
pageProps: PageProps;
isMobile: boolean;
theme: string;
__N_SSP: boolean;
-}
+};
-interface PageProps {
+type PageProps = {
initialReduxState: InitialReduxState;
book?: PagePropsBook;
totalData?: TotalData;
layoutViewCookies?: string;
displayBanner?: boolean;
chapter?: PagePropsChapter;
-}
+};
-interface PagePropsBook {
+type PagePropsBook = {
id: number;
countryId: number;
creatorId: number;
@@ -215,9 +215,9 @@ interface PagePropsBook {
verticalImage: Image;
horizontalImage: Image;
squareImage: Image;
-}
+};
-interface ChapterElement {
+type ChapterElement = {
id: number;
bookId: number;
title: string;
@@ -233,35 +233,35 @@ interface ChapterElement {
price: number;
views: number;
isUserPaid?: boolean;
-}
+};
-interface Country {
+type Country = {
id: number;
title: string;
code: string;
-}
+};
-interface Genre {
+type Genre = {
id: number;
title: string;
slug: string;
-}
+};
-interface Image {
+type Image = {
id: number;
alt: string;
path: string;
name: string;
url: string;
-}
+};
-interface Timer {
+type Timer = {
value: null;
for: null;
message: string;
-}
+};
-interface PagePropsChapter {
+type PagePropsChapter = {
id: number;
bookId: number;
editorId: null;
@@ -289,94 +289,94 @@ interface PagePropsChapter {
nextChapter: null;
previousChapter: PreviousChapter;
corrections: null;
-}
+};
-interface ChapterBook {
+type ChapterBook = {
id: number;
title: string;
titleEn: string;
url: string;
-}
+};
-interface Content {
+type Content = {
id: number;
text: string;
symbolsCount: number;
-}
+};
-interface PreviousChapter {
+type PreviousChapter = {
id: number;
title: string;
views: number;
publishedAt: Date;
url: string;
numberChapter: string;
-}
+};
-interface InitialReduxState {
+type InitialReduxState = {
data?: Data;
auth: Auth;
banners?: Banners;
corrections?: Corrections;
readerSettings?: ReaderSettings;
-}
+};
-interface Auth {
+type Auth = {
isAuth: boolean;
user: null;
-}
+};
-interface Banners {
+type Banners = {
displaySale: boolean;
-}
+};
-interface Corrections {
+type Corrections = {
items: null;
data: null;
-}
+};
-interface Data {
+type Data = {
bookPage: BookPage | null;
chapterPage: ChapterPage | null;
-}
+};
-interface BookPage {
+type BookPage = {
bookId: number;
bookmark: null;
-}
+};
-interface ChapterPage {
+type ChapterPage = {
chapterId: number;
bookId: number;
bookmark: null;
-}
+};
-interface ReaderSettings {
+type ReaderSettings = {
fontSize: number;
lineHeight: number;
textIndent: number;
sidebarHide: number;
textAlign: string;
-}
+};
-interface TotalData {
+type TotalData = {
items: Item[];
_links: Links;
pagesData: PagesData;
-}
+};
-interface Links {
+type Links = {
self: First;
first: First;
last: First;
next: First;
-}
+};
-interface First {
+type First = {
href: string;
-}
+};
-interface Item {
+type Item = {
id: number;
title: string;
description: string;
@@ -390,16 +390,16 @@ interface Item {
verticalImage: Image;
horizontalImage: Image;
squareImage: Image;
-}
+};
-interface PagesData {
+type PagesData = {
totalCount: number;
pageCount: number;
currentPage: number;
perPage: number;
-}
+};
-interface Query {
+type Query = {
book?: string;
chapter?: string;
-}
+};
diff --git a/src/plugins/russian/renovels.ts b/src/plugins/russian/renovels.ts
index 3a7aa71..1eb7d7b 100644
--- a/src/plugins/russian/renovels.ts
+++ b/src/plugins/russian/renovels.ts
@@ -946,7 +946,7 @@ class ReN implements Plugin.PluginBase {
export default new ReN();
-interface responseNovels {
+type responseNovels = {
id: number;
secondary_name: string;
main_name: string;
@@ -969,15 +969,15 @@ interface responseNovels {
categories?: ItemEntity[] | null;
rus_name?: string;
en_name?: string;
-}
+};
-interface Img {
+type Img = {
high?: string;
mid?: string;
low: string;
-}
+};
-interface responseNovel {
+type responseNovel = {
id: number;
img: Img;
secondary_name: string;
@@ -1010,42 +1010,42 @@ interface responseNovel {
uploaded: number;
can_post_comments: boolean;
adaptation: Adaptation | null;
-}
+};
-interface Adaptation {
+type Adaptation = {
main_name: string;
secondary_name: string;
dir: string;
img: string;
-}
+};
-interface ItemEntity {
+type ItemEntity = {
id: number;
name: string;
-}
-interface PublishersEntity {
+};
+type PublishersEntity = {
id: number;
name: string;
img: string;
dir: string;
tagline: string;
type: string;
-}
-interface BranchesEntity {
+};
+type BranchesEntity = {
id: number;
img: string;
publishers?: PublishersEntity[] | null;
subscribed: boolean;
total_votes: number;
count_chapters: number;
-}
-interface FirstChapter {
+};
+type FirstChapter = {
id: number;
tome: number;
chapter: string;
-}
+};
-interface responseСhapters {
+type responseСhapters = {
id: number;
rated?: null;
viewed?: null;
@@ -1060,9 +1060,9 @@ interface responseСhapters {
pub_date?: null;
upload_date: string;
is_paid: boolean;
-}
+};
-interface responseСhapter {
+type responseСhapter = {
id: number;
chapter: string;
name: string;
@@ -1078,4 +1078,4 @@ interface responseСhapter {
index: number;
publishers?: PublishersEntity[] | null;
tome: number;
-}
+};
diff --git a/src/plugins/russian/ruvers.ts b/src/plugins/russian/ruvers.ts
index 6ac1721..68e584c 100644
--- a/src/plugins/russian/ruvers.ts
+++ b/src/plugins/russian/ruvers.ts
@@ -125,7 +125,7 @@ class RV implements Plugin.PluginBase {
export default new RV();
-interface book {
+type book = {
id: number;
name: string;
original_name: string;
@@ -151,9 +151,9 @@ interface book {
updated_at: string;
slug: string;
chapters_count: number;
-}
+};
-interface chapters {
+type chapters = {
id: number;
name: string;
number: string;
@@ -173,7 +173,7 @@ interface chapters {
is_new: boolean;
purchased_by_user: boolean;
book_slug: string;
-}
+};
function unicodeToUtf8(unicode: string) {
return unicode.replace(/\\u([\d\w]{4})/gi, (match, hex) =>
diff --git a/src/plugins/spanish/skynovels.ts b/src/plugins/spanish/skynovels.ts
index 743bbcd..19a2951 100644
--- a/src/plugins/spanish/skynovels.ts
+++ b/src/plugins/spanish/skynovels.ts
@@ -111,10 +111,10 @@ class SkyNovels implements Plugin.PluginBase {
export default new SkyNovels();
-interface response {
+type response = {
novels?: NovelsEntity[] | null;
-}
-interface NovelsEntity {
+};
+type NovelsEntity = {
id: number;
nvl_author?: number | null;
nvl_content: string;
@@ -135,16 +135,16 @@ interface NovelsEntity {
nvl_rating?: number | null;
nvl_ratings_count: number;
genres?: GenresEntity[] | null;
-}
-interface GenresEntity {
+};
+type GenresEntity = {
id: number;
genre_name: string;
-}
+};
-interface responseBook {
+type responseBook = {
novel?: NovelEntity[] | null;
-}
-interface NovelEntity {
+};
+type NovelEntity = {
id: number;
nvl_author: number;
nvl_content: string;
@@ -168,29 +168,29 @@ interface NovelEntity {
novel_ratings?: NovelRatingsEntity[] | null;
collaborators?: CollaboratorsEntity[] | null;
genres?: GenresEntity[] | null;
-}
-interface BookmarksEntity {
+};
+type BookmarksEntity = {
id: number;
user_id: number;
chp_id: number;
chp_name: string;
-}
-interface VolumesEntity {
+};
+type VolumesEntity = {
vlm_title: string;
id: number;
nvl_id: number;
user_id?: number | null;
chapters?: ChaptersEntity[] | null;
-}
-interface ChaptersEntity {
+};
+type ChaptersEntity = {
id: number;
chp_index_title: string;
chp_name: string;
chp_number: number;
chp_status: string;
createdAt: string;
-}
-interface NovelRatingsEntity {
+};
+type NovelRatingsEntity = {
user_id: number;
rate_value: number;
rate_comment: string;
@@ -201,21 +201,21 @@ interface NovelRatingsEntity {
user_login: string;
image?: string | null;
likes?: (LikesEntity | null)[] | null;
-}
-interface LikesEntity {
+};
+type LikesEntity = {
id: number;
user_id: number;
user_login: string;
-}
-interface CollaboratorsEntity {
+};
+type CollaboratorsEntity = {
user_id: number;
user_login: string;
-}
+};
-interface responseChapter {
+type responseChapter = {
chapter?: ChapterEntity[] | null;
-}
-interface ChapterEntity {
+};
+type ChapterEntity = {
id: number;
chp_author: number;
chp_translator?: null;
@@ -237,9 +237,9 @@ interface ChapterEntity {
comments?: null[] | null;
reactions?: null[] | null;
total_reactions?: TotalReactionsEntity[] | null;
-}
-interface TotalReactionsEntity {
+};
+type TotalReactionsEntity = {
reaction_id: number;
reaction_name: string;
reaction_count: number;
-}
+};
diff --git a/src/plugins/turkish/lshnovel.broken.ts b/src/plugins/turkish/lshnovel.broken.ts
index e9590b4..5e6c7d8 100644
--- a/src/plugins/turkish/lshnovel.broken.ts
+++ b/src/plugins/turkish/lshnovel.broken.ts
@@ -140,7 +140,7 @@ class LSHNovel implements Plugin.PluginBase {
async searchNovels(
searchTerm: string,
- pageNo?: number | undefined,
+ //pageNo?: number | undefined,
): Promise {
const url = `${this.site}?s=${searchTerm}`;
const headers = new Headers();
diff --git a/src/plugins/ukrainian/smakolykytl.ts b/src/plugins/ukrainian/smakolykytl.ts
index 1f024d9..b8b5696 100644
--- a/src/plugins/ukrainian/smakolykytl.ts
+++ b/src/plugins/ukrainian/smakolykytl.ts
@@ -142,55 +142,55 @@ function jsonToHtml(json: HTML[], html = '') {
return html;
}
-interface response {
+type response = {
projects?: TopLevelProject[];
updates?: Update[];
project?: TopLevelProject;
books?: BookElement[];
chapter?: TopLevelChapter;
-}
+};
-interface BookElement {
+type BookElement = {
id: number;
rank: number;
title: string;
chapters: PurpleChapter[];
-}
+};
-interface PurpleChapter {
+type PurpleChapter = {
id: number;
title: string;
rank: string;
modifiedAt: Date;
-}
+};
-interface TopLevelChapter {
+type TopLevelChapter = {
id: number;
title: string;
rank: string;
content: string;
modifiedAt: Date;
book: ChapterBook;
-}
+};
-interface ChapterBook {
+type ChapterBook = {
id: number;
rank: number;
title: string;
chapters: FluffyChapter[];
project: BookProject;
-}
+};
-interface FluffyChapter {
+type FluffyChapter = {
id: number;
rank: string;
-}
+};
-interface BookProject {
+type BookProject = {
id: number;
-}
+};
-interface TopLevelProject {
+type TopLevelProject = {
id: number;
title: string;
description: string;
@@ -205,37 +205,37 @@ interface TopLevelProject {
image: Image;
tags?: Genre[];
genres?: Genre[];
-}
+};
-interface Genre {
+type Genre = {
id: number;
title: string;
-}
+};
-interface Image {
+type Image = {
id: number;
url: string;
name: string;
-}
+};
-interface Update {
+type Update = {
id: number;
title: string;
author: string;
translator: string;
modifiedAt: Date;
image: Image;
-}
+};
-interface HTML {
+type HTML = {
type: string;
content?: HTML[];
attrs?: Attrs;
text?: string;
-}
+};
-interface Attrs {
+type Attrs = {
src: string;
alt: string | null;
title: string | null;
-}
+};
diff --git a/src/plugins/vietnamese/Truyenconect.ts b/src/plugins/vietnamese/Truyenconect.ts
index 5759cf9..540a901 100644
--- a/src/plugins/vietnamese/Truyenconect.ts
+++ b/src/plugins/vietnamese/Truyenconect.ts
@@ -4,21 +4,21 @@ import { Plugin } from '@typings/plugin';
import { FilterTypes, Filters } from '@libs/filterInputs';
import { NovelStatus } from '@libs/novelStatus';
-interface VolumeData {
+type VolumeData = {
story: string;
navigation: string;
value: string;
-}
+};
-interface ChapterSelect {
+type ChapterSelect = {
chap_selector: string;
eps_selector: boolean | string;
-}
+};
-interface VolumeList {
+type VolumeList = {
chapters: Plugin.ChapterItem[];
volumes?: VolumeData[];
-}
+};
class TruyenConect implements Plugin.PagePlugin {
id = 'truyenconect';
diff --git a/src/plugins/vietnamese/lightnovelvn.ts b/src/plugins/vietnamese/lightnovelvn.ts
index 5ffaaee..3326dff 100644
--- a/src/plugins/vietnamese/lightnovelvn.ts
+++ b/src/plugins/vietnamese/lightnovelvn.ts
@@ -4,14 +4,14 @@ import { Plugin } from '@typings/plugin';
import { Filters } from '@libs/filterInputs';
import { NovelStatus } from '@libs/novelStatus';
-interface SearchedNovel {
+type SearchedNovel = {
name: string;
slug: string;
coverUrl: string;
-}
-interface SearchedResult {
+};
+type SearchedResult = {
data?: SearchedNovel[];
-}
+};
class LightNovelVN implements Plugin.PagePlugin {
id = 'lightnovel.vn';
diff --git a/src/provider/DialogProvider/index.tsx b/src/provider/DialogProvider/index.tsx
index f8b20f9..1a14d02 100644
--- a/src/provider/DialogProvider/index.tsx
+++ b/src/provider/DialogProvider/index.tsx
@@ -1,11 +1,8 @@
-import { AppState } from '@redux/store';
import React from 'react';
-import { useSelector } from 'react-redux';
+// import { useAppStore } from '@store';
export default function DialogProvider() {
- const activeDialogs = useSelector(
- (state: AppState) => state.dialog.activeDialogs,
- );
+ // const _activeDialogs = useAppStore(state => state.activeDialogs);
return <>>;
}
diff --git a/src/provider/index.tsx b/src/provider/index.tsx
index 4e1f499..8dbb044 100644
--- a/src/provider/index.tsx
+++ b/src/provider/index.tsx
@@ -1,11 +1,10 @@
import React from 'react';
import DialogProvider from './DialogProvider';
import { Backdrop, Box, CircularProgress, Container } from '@mui/material';
-import { useSelector } from 'react-redux';
-import { AppState } from '@redux/store';
+import { useAppStore } from '@store';
export default function AppProvider() {
- const loading = useSelector((state: AppState) => state.overlay.loading);
+ const loading = useAppStore(state => state.loading);
return (
) {
- state.activeDialogs = [...state.activeDialogs, action.payload];
- },
- hideDialog(state, action: PayloadAction) {
- state.activeDialogs = state.activeDialogs.filter(
- dialog => dialog !== action.payload,
- );
- },
- hideAllDialogs(state) {
- state.activeDialogs = [];
- },
- },
-});
-
-export const { showDialog, hideDialog } = dialogSlice.actions;
-
-export default dialogSlice.reducer;
diff --git a/src/redux/overlaySlice.ts b/src/redux/overlaySlice.ts
deleted file mode 100644
index 6b7f2e0..0000000
--- a/src/redux/overlaySlice.ts
+++ /dev/null
@@ -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;
diff --git a/src/redux/pluginSlice.ts b/src/redux/pluginSlice.ts
deleted file mode 100644
index 9150f10..0000000
--- a/src/redux/pluginSlice.ts
+++ /dev/null
@@ -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) {
- state.selected = acion.payload;
- },
- },
-});
-
-export const { selectPlugin } = pluginSlice.actions;
-
-export default pluginSlice.reducer;
diff --git a/src/redux/store.ts b/src/redux/store.ts
deleted file mode 100644
index 537bd15..0000000
--- a/src/redux/store.ts
+++ /dev/null
@@ -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;
-export type AppDispatch = typeof store.dispatch;
diff --git a/src/store/dialogStore.ts b/src/store/dialogStore.ts
new file mode 100644
index 0000000..e964ecb
--- /dev/null
+++ b/src/store/dialogStore.ts
@@ -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 = 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: [],
+ }));
+ },
+});
diff --git a/src/store/index.ts b/src/store/index.ts
new file mode 100644
index 0000000..35babcf
--- /dev/null
+++ b/src/store/index.ts
@@ -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>[0];
+export type GetStore = Parameters>[1];
+export type StoreCreator = (s: SetStore, g: GetStore) => T;
+
+export const useAppStore = create((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');
+ },
+}));
diff --git a/src/store/overlayStore.ts b/src/store/overlayStore.ts
new file mode 100644
index 0000000..71be2b0
--- /dev/null
+++ b/src/store/overlayStore.ts
@@ -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 = set => ({
+ // this is initial state
+ loading: false,
+
+ // those are actions
+ showLoading() {
+ set(state => ({
+ ...state,
+ loading: true,
+ }));
+ },
+ hideLoading() {
+ set(state => ({
+ ...state,
+ loading: false,
+ }));
+ },
+});
diff --git a/src/store/pluginStore.ts b/src/store/pluginStore.ts
new file mode 100644
index 0000000..c154e1d
--- /dev/null
+++ b/src/store/pluginStore.ts
@@ -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 = set => ({
+ // this is initial state
+ // selectedPlugin: undefined,
+
+ // those are actions
+ selectPlugin(pluginItem) {
+ set(state => ({
+ ...state,
+ pluginItem,
+ plugin: getPlugin(pluginItem.id),
+ }));
+ },
+});
diff --git a/src/types/plugin.ts b/src/types/plugin.ts
index 8f0a547..83d456e 100644
--- a/src/types/plugin.ts
+++ b/src/types/plugin.ts
@@ -1,6 +1,6 @@
import { FilterToValues, Filters } from '@libs/filterInputs';
export namespace Plugin {
- export interface ChapterItem {
+ export type ChapterItem = {
name: string;
path: string;
/**
@@ -17,13 +17,13 @@ export namespace Plugin {
* For novel without pages only
*/
page?: string;
- }
- export interface NovelItem {
+ };
+ export type NovelItem = {
name: string;
path: string;
cover?: string;
- }
- export interface SourceNovel extends NovelItem {
+ };
+ export type SourceNovel = {
/** Comma separated genre list -> "action,fantasy,romance" */
genres?: string;
summary?: string;
@@ -31,32 +31,32 @@ export namespace Plugin {
artist?: string;
status?: string;
chapters?: ChapterItem[];
- }
+ } & NovelItem;
- export interface SourcePage {
+ export type SourcePage = {
chapters: ChapterItem[];
- }
+ };
- export interface PopularNovelsOptions<
+ export type PopularNovelsOptions<
Q extends Filters | undefined = Filters | undefined,
- > {
+ > = {
showLatestNovels?: boolean;
- filters?: Q extends undefined ? undefined : FilterToValues;
- }
- export interface PluginItem {
+ filters: Q extends undefined ? undefined : FilterToValues;
+ };
+ export type PluginItem = {
id: string;
name: string;
version: string;
icon: string;
site: string;
- }
- export interface ImageRequestInit {
+ };
+ export type ImageRequestInit = {
method?: string;
headers?: Record;
body?: string;
- }
+ };
- export interface PluginBase {
+ export type PluginBase = {
id: string;
name: string;
/**
@@ -87,32 +87,31 @@ export namespace Plugin {
parseChapter(chapterPath: string): Promise;
searchNovels(searchTerm: string, pageNo: number): Promise;
resolveUrl?(path: string, isNovel?: boolean): string;
- }
+ };
- export interface PagePlugin extends PluginBase {
+ export type PagePlugin = {
parseNovel(
novelPath: string,
): Promise;
parsePage(novelPath: string, page: string): Promise;
- }
+ } & PluginBase;
}
export namespace HTMLParser2Util {
- interface HandlerBase {
+ type HandlerBase = {
onopentag?(name: string, attribs: Record): void;
ontext?(data: string): void;
onclosetag?(name: string, isImplied: boolean): void;
- }
+ };
- export interface Handler extends HandlerBase {
+ export type Handler = {
isStarted?: boolean;
isDone?: boolean;
- }
+ } & HandlerBase;
// route htmlparser2 event to handlers
- export interface HandlerRouter
- extends HandlerBase {
+ export type HandlerRouter = {
handlers: Record;
action: ActionType;
- }
+ } & HandlerBase;
}
diff --git a/tsconfig.json b/tsconfig.json
index 6e225b9..d27f2c2 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,128 +1,129 @@
{
- "compilerOptions": {
- "jsx": "react",
- /* Visit https://aka.ms/tsconfig to read more about this file */
+ "compilerOptions": {
+ "jsx": "react",
+ /* Visit https://aka.ms/tsconfig to read more about this file */
- /* 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. */
- // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
- // "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. */
- // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+ /* 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. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "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. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
- /* Language and Environment */
- "target": "ES5" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
- // "lib": [
- // "es2016",
- // "dom",
- // "es5"
- // ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
- // "jsx": "preserve", /* Specify what JSX code is generated. */
- // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
- // "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'. */
- // "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*'. */
- // "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. */
- // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
- // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+ /* Language and Environment */
+ "target": "ES5" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
+ // "lib": [
+ // "es2016",
+ // "dom",
+ // "es5"
+ // ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
+ // "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'. */
+ // "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*'. */
+ // "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. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
- /* Modules */
- "module": "ES2020" /* Specify what module code is generated. */,
- // "rootDir": "./", /* Specify the root folder within your source files. */
- "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. */
- "paths": {
- "@libs/*": ["./src/libs/*"],
- "@icons/*": ["./src/icons/*"],
- "@components/*": ["./src/components/*"],
- "@redux/*": ["./src/redux/*"],
- "@provider/*": ["./src/provider/*"],
- "@plugins/*": ["./src/plugins/*"],
- "@hooks/*": ["./src/hooks/*"],
- "@scripts/*": ["./scripts/*"],
- "@typings/*": ["./src/types/*"]
- } /* Specify a set of entries that re-map imports to additional lookup locations. */,
- // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
- // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
- // "types": [], /* Specify type package names to be included without being referenced in a source file. */
- // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
- // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
- // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
- // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
- // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
- // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
- "resolveJsonModule": true /* Enable importing .json files. */,
- // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
- // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+ /* Modules */
+ "module": "ES2020" /* Specify what module code is generated. */,
+ // "rootDir": "./", /* Specify the root folder within your source files. */
+ "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. */,
+ "paths": {
+ "@libs/*": ["./src/libs/*"],
+ "@icons/*": ["./src/icons/*"],
+ "@components/*": ["./src/components/*"],
+ "@redux/*": ["./src/redux/*"],
+ "@provider/*": ["./src/provider/*"],
+ "@plugins/*": ["./src/plugins/*"],
+ "@hooks/*": ["./src/hooks/*"],
+ "@scripts/*": ["./scripts/*"],
+ "@typings/*": ["./src/types/*"],
+ "@store": ["./src/store"]
+ } /* Specify a set of entries that re-map imports to additional lookup locations. */,
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
+ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+ "resolveJsonModule": true /* Enable importing .json files. */,
+ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
- /* JavaScript Support */
- // "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. */,
- // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+ /* JavaScript Support */
+ // "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. */,
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
- /* Emit */
- // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
- // "declarationMap": true, /* Create sourcemaps for d.ts files. */
- // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
- // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
- // "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. */
- "outDir": "./.js/" /* Specify an output folder for all emitted files. */,
- // "removeComments": true, /* Disable emitting comments. */
- // "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. */
- // "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. */
- // "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. */
- // "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. */
- // "newLine": "crlf", /* Set the newline character for emitting files. */
- // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
- // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
- // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
- // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
- // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
- // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
- "allowJs": true,
- "checkJs": false,
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "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. */
+ "outDir": "./.js/" /* Specify an output folder for all emitted files. */,
+ // "removeComments": true, /* Disable emitting comments. */
+ // "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. */
+ // "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. */
+ // "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. */
+ // "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. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+ "allowJs": true,
+ "checkJs": false,
- /* Interop Constraints */
- // "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. */
- // "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. */,
- // "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. */,
+ /* Interop Constraints */
+ // "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. */
+ // "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. */,
+ // "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. */,
- /* Type Checking */
- "strict": true /* Enable all strict type-checking options. */,
- // "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'. */
- // "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. */
- // "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'. */
- // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
- // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
- // "noUnusedLocals": true, /* Enable error reporting when local variables aren'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'. */
- // "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. */
- // "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. */
- // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
- // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
- // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+ /* Type Checking */
+ "strict": true /* Enable all strict type-checking options. */,
+ // "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'. */
+ // "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. */
+ // "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'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren'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'. */
+ // "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. */
+ // "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. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
- /* Completeness */
- // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
- "skipLibCheck": true /* Skip type checking all .d.ts files. */
- },
- "exclude": ["./src/plugins/**/*.broken.ts", "./.js/**/*", "scripts", "docs"]
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ },
+ "exclude": ["./src/plugins/**/*.broken.ts", "./.js/**/*", "scripts", "docs"]
}
diff --git a/vite.config.js b/vite.config.js
index 7e67cb5..91adf3c 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -19,6 +19,7 @@ export default defineConfig({
'@hooks': path.resolve(dirname, './src/hooks'),
'@typings': path.resolve(dirname, './src/types'),
'@scripts': path.resolve(dirname, './scripts'),
+ '@store': path.resolve(dirname, './src/store'),
},
},
server: {