Implement Unread Filter for Library (#54)
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
"@mui/system": "^5.0.6",
|
"@mui/system": "^5.0.6",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"file-selector": "^0.2.4",
|
"file-selector": "^0.2.4",
|
||||||
|
"query-string": "^7.0.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-beautiful-dnd": "^13.0.0",
|
"react-beautiful-dnd": "^13.0.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
@@ -20,6 +21,7 @@
|
|||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "4.0.3",
|
||||||
"react-virtuoso": "^1.8.6",
|
"react-virtuoso": "^1.8.6",
|
||||||
|
"use-query-params": "^1.2.3",
|
||||||
"web-vitals": "^2.1.0"
|
"web-vitals": "^2.1.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
30
src/App.tsx
30
src/App.tsx
@@ -11,6 +11,7 @@ import {
|
|||||||
Route,
|
Route,
|
||||||
Redirect,
|
Redirect,
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
|
import { QueryParamProvider } from 'use-query-params';
|
||||||
import { Container, useMediaQuery } from '@mui/material';
|
import { Container, useMediaQuery } from '@mui/material';
|
||||||
import CssBaseline from '@mui/material/CssBaseline';
|
import CssBaseline from '@mui/material/CssBaseline';
|
||||||
import {
|
import {
|
||||||
@@ -36,20 +37,32 @@ import Browse from 'screens/manga/Browse';
|
|||||||
|
|
||||||
declare module '@mui/styles/defaultTheme' {
|
declare module '@mui/styles/defaultTheme' {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
interface DefaultTheme extends Theme {}
|
interface DefaultTheme extends Theme {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [title, setTitle] = useState<string>('Tachidesk');
|
const [title, setTitle] = useState<string>('Tachidesk');
|
||||||
const [action, setAction] = useState<any>(<div />);
|
const [action, setAction] = useState<any>(<div />);
|
||||||
const [override, setOverride] = useState<INavbarOverride>({ status: false, value: <div /> });
|
const [override, setOverride] = useState<INavbarOverride>({
|
||||||
|
status: false,
|
||||||
|
value: <div />,
|
||||||
|
});
|
||||||
|
|
||||||
const [darkTheme, setDarkTheme] = useLocalStorage<boolean>('darkTheme', true);
|
const [darkTheme, setDarkTheme] = useLocalStorage<boolean>('darkTheme', true);
|
||||||
|
|
||||||
const navBarContext = {
|
const navBarContext = {
|
||||||
title, setTitle, action, setAction, override, setOverride,
|
title,
|
||||||
|
setTitle,
|
||||||
|
action,
|
||||||
|
setAction,
|
||||||
|
override,
|
||||||
|
setOverride,
|
||||||
|
};
|
||||||
|
const darkThemeContext = {
|
||||||
|
darkTheme,
|
||||||
|
setDarkTheme,
|
||||||
};
|
};
|
||||||
const darkThemeContext = { darkTheme, setDarkTheme };
|
|
||||||
|
|
||||||
const theme = React.useMemo(
|
const theme = React.useMemo(
|
||||||
() => createTheme({
|
() => createTheme({
|
||||||
@@ -82,6 +95,7 @@ export default function App() {
|
|||||||
<Router>
|
<Router>
|
||||||
<StyledEngineProvider injectFirst>
|
<StyledEngineProvider injectFirst>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
|
<QueryParamProvider ReactRouterRoute={Route}>
|
||||||
<NavbarContext.Provider value={navBarContext}>
|
<NavbarContext.Provider value={navBarContext}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<NavBar />
|
<NavBar />
|
||||||
@@ -106,7 +120,6 @@ export default function App() {
|
|||||||
<Redirect to="/library" />
|
<Redirect to="/library" />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route path="/settings/about">
|
<Route path="/settings/about">
|
||||||
<About />
|
<About />
|
||||||
</Route>
|
</Route>
|
||||||
@@ -161,11 +174,16 @@ export default function App() {
|
|||||||
path="/manga/:mangaId/chapter/:chapterIndex"
|
path="/manga/:mangaId/chapter/:chapterIndex"
|
||||||
// passing a key re-mounts the reader when changing chapters
|
// passing a key re-mounts the reader when changing chapters
|
||||||
render={
|
render={
|
||||||
(props:any) => <Reader key={props.match.params.chapterIndex} />
|
(props: any) => (
|
||||||
|
<Reader
|
||||||
|
key={props.match.params.chapterIndex}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
</NavbarContext.Provider>
|
</NavbarContext.Provider>
|
||||||
|
</QueryParamProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</StyledEngineProvider>
|
</StyledEngineProvider>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
90
src/components/ThreeStateCheckbox.tsx
Normal file
90
src/components/ThreeStateCheckbox.tsx
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Checkbox, createSvgIcon } from '@mui/material';
|
||||||
|
import React, {
|
||||||
|
useEffect, useState,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
export interface IThreeStateCheckboxProps {
|
||||||
|
name: string
|
||||||
|
checked: boolean | undefined | null
|
||||||
|
onChange: (change: boolean | undefined | null) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CheckState {
|
||||||
|
SELECTED, INTERMEDIATE, UNSELECTED,
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkedToState(checked: boolean | undefined | null): CheckState {
|
||||||
|
switch (checked) {
|
||||||
|
case true:
|
||||||
|
return CheckState.SELECTED;
|
||||||
|
case false:
|
||||||
|
return CheckState.INTERMEDIATE;
|
||||||
|
default:
|
||||||
|
return CheckState.UNSELECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function stateToChecked(state: CheckState): boolean | undefined {
|
||||||
|
switch (state) {
|
||||||
|
case CheckState.SELECTED:
|
||||||
|
return true;
|
||||||
|
case CheckState.INTERMEDIATE:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
case CheckState.UNSELECTED:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stateTransition(state: CheckState): CheckState {
|
||||||
|
switch (state) {
|
||||||
|
case CheckState.SELECTED:
|
||||||
|
return CheckState.INTERMEDIATE;
|
||||||
|
case CheckState.INTERMEDIATE:
|
||||||
|
return CheckState.UNSELECTED;
|
||||||
|
case CheckState.UNSELECTED:
|
||||||
|
default:
|
||||||
|
return CheckState.SELECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThreeStateCheckbox = (props: IThreeStateCheckboxProps) => {
|
||||||
|
const {
|
||||||
|
name, checked, onChange,
|
||||||
|
} = props;
|
||||||
|
const [localChecked, setLocalChecked] = useState(checkedToState(checked));
|
||||||
|
useEffect(() => setLocalChecked(checkedToState(checked)), [checked]);
|
||||||
|
const handleChange = () => {
|
||||||
|
setLocalChecked(stateTransition(localChecked));
|
||||||
|
if (onChange) {
|
||||||
|
onChange(stateToChecked(stateTransition(localChecked)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const CancelBox = createSvgIcon(
|
||||||
|
<>
|
||||||
|
<path
|
||||||
|
d="M 19 6.41 L 13.41 12 L 19 17.59 L 17.59 19 L 12 13.41 L 6.41 19 V 19 H 6.41 L 5 17.59 L 11 12 L 5 6.41 L 6.41 5 L 12 10.59 L 17.59 5 L 19 6.41 M 5 5 m 0 -2 H 5 c -1.1 0 -2 0.9 -2 2 v 14 c 0 1.1 0.9 2 2 2 h 14 c 1.1 0 2 -0.9 2 -2 V 5 c 0 -1.1 -0.9 -2 -2 -2 z "
|
||||||
|
/>
|
||||||
|
</>,
|
||||||
|
'CancelBox',
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Checkbox
|
||||||
|
name={name}
|
||||||
|
checked={localChecked === CheckState.SELECTED}
|
||||||
|
indeterminate={localChecked === CheckState.INTERMEDIATE}
|
||||||
|
indeterminateIcon={<CancelBox />}
|
||||||
|
onChange={handleChange}
|
||||||
|
className={`${localChecked}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default ThreeStateCheckbox;
|
||||||
51
src/components/library/LibraryMangaGrid.tsx
Normal file
51
src/components/library/LibraryMangaGrid.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import MangaGrid, { IMangaGridProps } from '../manga/MangaGrid';
|
||||||
|
import useLibraryOptions, { NullAndUndefined } from '../../util/useLibraryOptions';
|
||||||
|
|
||||||
|
const FILTERED_OUT_MESSAGE = 'There are no Manga matching this filter';
|
||||||
|
|
||||||
|
function unreadFilter(unread: NullAndUndefined<boolean>, { unreadCount }: IMangaCard): boolean {
|
||||||
|
switch (unread) {
|
||||||
|
case true:
|
||||||
|
return !!unreadCount && unreadCount >= 1;
|
||||||
|
case false:
|
||||||
|
return unreadCount === 0;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterManga(mangas: IMangaCard[]): IMangaCard[] {
|
||||||
|
const { unread } = useLibraryOptions();
|
||||||
|
return mangas
|
||||||
|
.filter((manga) => unreadFilter(unread, manga));
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LibraryMangaGrid(props: IMangaGridProps) {
|
||||||
|
const {
|
||||||
|
mangas, isLoading, hasNextPage, lastPageNum, setLastPageNum, message,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const { active } = useLibraryOptions();
|
||||||
|
const filteredManga = filterManga(mangas);
|
||||||
|
const showFilteredOutMessage = active && filteredManga.length === 0 && mangas.length > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MangaGrid
|
||||||
|
mangas={filteredManga}
|
||||||
|
isLoading={isLoading}
|
||||||
|
hasNextPage={hasNextPage}
|
||||||
|
lastPageNum={lastPageNum}
|
||||||
|
setLastPageNum={setLastPageNum}
|
||||||
|
message={showFilteredOutMessage ? FILTERED_OUT_MESSAGE : message}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
51
src/components/library/LibraryOptions.tsx
Normal file
51
src/components/library/LibraryOptions.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import FilterListIcon from '@mui/icons-material/FilterList';
|
||||||
|
import { Drawer, FormControlLabel, IconButton } from '@mui/material';
|
||||||
|
import useLibraryOptions from '../../util/useLibraryOptions';
|
||||||
|
import ThreeStateCheckbox from '../ThreeStateCheckbox';
|
||||||
|
|
||||||
|
function Options() {
|
||||||
|
const { unread, setUnread } = useLibraryOptions();
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FormControlLabel control={<ThreeStateCheckbox name="Unread" checked={unread} onChange={setUnread} />} label="Unread" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LibraryOptions() {
|
||||||
|
const [filtersOpen, setFiltersOpen] = React.useState(false);
|
||||||
|
const { active } = useLibraryOptions();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => setFiltersOpen(!filtersOpen)}
|
||||||
|
color={active ? 'warning' : 'default'}
|
||||||
|
>
|
||||||
|
<FilterListIcon />
|
||||||
|
</IconButton>
|
||||||
|
|
||||||
|
<Drawer
|
||||||
|
anchor="bottom"
|
||||||
|
open={filtersOpen}
|
||||||
|
onClose={() => setFiltersOpen(false)}
|
||||||
|
PaperProps={{
|
||||||
|
style: {
|
||||||
|
maxWidth: 600, padding: '1em', marginLeft: 'auto', marginRight: 'auto',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Options />
|
||||||
|
</Drawer>
|
||||||
|
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ import EmptyView from 'components/EmptyView';
|
|||||||
import LoadingPlaceholder from 'components/LoadingPlaceholder';
|
import LoadingPlaceholder from 'components/LoadingPlaceholder';
|
||||||
import MangaCard from './MangaCard';
|
import MangaCard from './MangaCard';
|
||||||
|
|
||||||
interface IProps{
|
export interface IMangaGridProps{
|
||||||
mangas: IMangaCard[]
|
mangas: IMangaCard[]
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
message?: string
|
message?: string
|
||||||
@@ -21,7 +21,7 @@ interface IProps{
|
|||||||
setLastPageNum: (lastPageNum: number) => void
|
setLastPageNum: (lastPageNum: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MangaGrid(props: IProps) {
|
export default function MangaGrid(props: IMangaGridProps) {
|
||||||
const {
|
const {
|
||||||
mangas, isLoading, message, messageExtra, hasNextPage, lastPageNum, setLastPageNum,
|
mangas, isLoading, message, messageExtra, hasNextPage, lastPageNum, setLastPageNum,
|
||||||
} = props;
|
} = props;
|
||||||
|
|||||||
@@ -7,13 +7,14 @@
|
|||||||
|
|
||||||
import { Tab, Tabs } from '@mui/material';
|
import { Tab, Tabs } from '@mui/material';
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import MangaGrid from 'components/manga/MangaGrid';
|
|
||||||
import NavbarContext from 'context/NavbarContext';
|
import NavbarContext from 'context/NavbarContext';
|
||||||
import client from 'util/client';
|
import client from 'util/client';
|
||||||
import cloneObject from 'util/cloneObject';
|
import cloneObject from 'util/cloneObject';
|
||||||
import EmptyView from 'components/EmptyView';
|
import EmptyView from 'components/EmptyView';
|
||||||
import LoadingPlaceholder from 'components/LoadingPlaceholder';
|
import LoadingPlaceholder from 'components/LoadingPlaceholder';
|
||||||
import TabPanel from 'components/util/TabPanel';
|
import TabPanel from 'components/util/TabPanel';
|
||||||
|
import LibraryOptions from '../../components/library/LibraryOptions';
|
||||||
|
import LibraryMangaGrid from '../../components/library/LibraryMangaGrid';
|
||||||
|
|
||||||
interface IMangaCategory {
|
interface IMangaCategory {
|
||||||
category: ICategory
|
category: ICategory
|
||||||
@@ -23,7 +24,13 @@ interface IMangaCategory {
|
|||||||
|
|
||||||
export default function Library() {
|
export default function Library() {
|
||||||
const { setTitle, setAction } = useContext(NavbarContext);
|
const { setTitle, setAction } = useContext(NavbarContext);
|
||||||
useEffect(() => { setTitle('Library'); setAction(<></>); }, []);
|
useEffect(() => {
|
||||||
|
setTitle('Library'); setAction(
|
||||||
|
<>
|
||||||
|
<LibraryOptions />
|
||||||
|
</>,
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const [tabs, setTabs] = useState<IMangaCategory[]>();
|
const [tabs, setTabs] = useState<IMangaCategory[]>();
|
||||||
const [tabNum, setTabNum] = useState<number>(0);
|
const [tabNum, setTabNum] = useState<number>(0);
|
||||||
@@ -88,7 +95,7 @@ export default function Library() {
|
|||||||
|
|
||||||
const tabBodies = tabs.map((tab) => (
|
const tabBodies = tabs.map((tab) => (
|
||||||
<TabPanel index={tab.category.order} currentIndex={tabNum}>
|
<TabPanel index={tab.category.order} currentIndex={tabNum}>
|
||||||
<MangaGrid
|
<LibraryMangaGrid
|
||||||
mangas={tab.mangas}
|
mangas={tab.mangas}
|
||||||
hasNextPage={false}
|
hasNextPage={false}
|
||||||
lastPageNum={lastPageNum}
|
lastPageNum={lastPageNum}
|
||||||
@@ -122,7 +129,7 @@ export default function Library() {
|
|||||||
} else {
|
} else {
|
||||||
const mangas = tabs.length === 1 ? tabs[0].mangas : [];
|
const mangas = tabs.length === 1 ? tabs[0].mangas : [];
|
||||||
toRender = (
|
toRender = (
|
||||||
<MangaGrid
|
<LibraryMangaGrid
|
||||||
mangas={mangas}
|
mangas={mangas}
|
||||||
hasNextPage={false}
|
hasNextPage={false}
|
||||||
lastPageNum={lastPageNum}
|
lastPageNum={lastPageNum}
|
||||||
|
|||||||
32
src/util/useLibraryOptions.ts
Normal file
32
src/util/useLibraryOptions.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BooleanParam, useQueryParams } from 'use-query-params';
|
||||||
|
|
||||||
|
export type NullAndUndefined<T> = T | null | undefined;
|
||||||
|
|
||||||
|
interface IUseLibraryOptions {
|
||||||
|
unread: NullAndUndefined<boolean>
|
||||||
|
setUnread: (unread: NullAndUndefined<boolean>) => void
|
||||||
|
active: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function useLibraryOptions(): IUseLibraryOptions {
|
||||||
|
const [query, setQuery] = useQueryParams({
|
||||||
|
unread: BooleanParam,
|
||||||
|
});
|
||||||
|
const { unread } = query;
|
||||||
|
const setUnread = (newUnread: NullAndUndefined<boolean>) => {
|
||||||
|
setQuery(Object.assign(query, { unread: newUnread }), 'replace');
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line eqeqeq
|
||||||
|
const active = !(unread == undefined);
|
||||||
|
return {
|
||||||
|
unread, setUnread, active,
|
||||||
|
};
|
||||||
|
}
|
||||||
37
yarn.lock
37
yarn.lock
@@ -5435,6 +5435,11 @@ fill-range@^7.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
to-regex-range "^5.0.1"
|
to-regex-range "^5.0.1"
|
||||||
|
|
||||||
|
filter-obj@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b"
|
||||||
|
integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs=
|
||||||
|
|
||||||
finalhandler@~1.1.2:
|
finalhandler@~1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
|
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
|
||||||
@@ -9478,6 +9483,16 @@ query-string@^4.1.0:
|
|||||||
object-assign "^4.1.0"
|
object-assign "^4.1.0"
|
||||||
strict-uri-encode "^1.0.0"
|
strict-uri-encode "^1.0.0"
|
||||||
|
|
||||||
|
query-string@^7.0.1:
|
||||||
|
version "7.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.0.1.tgz#45bd149cf586aaa582dffc7ec7a8ad97dd02f75d"
|
||||||
|
integrity sha512-uIw3iRvHnk9to1blJCG3BTc+Ro56CBowJXKmNNAm3RulvPBzWLRqKSiiDk+IplJhsydwtuNMHi8UGQFcCLVfkA==
|
||||||
|
dependencies:
|
||||||
|
decode-uri-component "^0.2.0"
|
||||||
|
filter-obj "^1.1.0"
|
||||||
|
split-on-first "^1.0.0"
|
||||||
|
strict-uri-encode "^2.0.0"
|
||||||
|
|
||||||
querystring-es3@^0.2.0:
|
querystring-es3@^0.2.0:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
||||||
@@ -10361,6 +10376,11 @@ serialize-javascript@^5.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
randombytes "^2.1.0"
|
randombytes "^2.1.0"
|
||||||
|
|
||||||
|
serialize-query-params@^1.3.5:
|
||||||
|
version "1.3.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/serialize-query-params/-/serialize-query-params-1.3.5.tgz#ca6a6419219ead24cf3b55d7b4d6a4b716b70359"
|
||||||
|
integrity sha512-BrLH1RqgzVxm6dco+KP9S6BodeFiUVvKwtY3GSWQlupIdblT19KCGTRkHZ2yIU6Bjy0Prjts0tYe11VpTMbAeQ==
|
||||||
|
|
||||||
serve-index@^1.9.1:
|
serve-index@^1.9.1:
|
||||||
version "1.9.1"
|
version "1.9.1"
|
||||||
resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
|
resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
|
||||||
@@ -10657,6 +10677,11 @@ spdy@^4.0.2:
|
|||||||
select-hose "^2.0.0"
|
select-hose "^2.0.0"
|
||||||
spdy-transport "^3.0.0"
|
spdy-transport "^3.0.0"
|
||||||
|
|
||||||
|
split-on-first@^1.0.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
|
||||||
|
integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
|
||||||
|
|
||||||
split-string@^3.0.1, split-string@^3.0.2:
|
split-string@^3.0.1, split-string@^3.0.2:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
|
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
|
||||||
@@ -10750,6 +10775,11 @@ strict-uri-encode@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
||||||
integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
|
integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=
|
||||||
|
|
||||||
|
strict-uri-encode@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
|
||||||
|
integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
|
||||||
|
|
||||||
string-length@^4.0.1:
|
string-length@^4.0.1:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"
|
resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"
|
||||||
@@ -11459,6 +11489,13 @@ use-memo-one@^1.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.2.tgz#0c8203a329f76e040047a35a1197defe342fab20"
|
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.2.tgz#0c8203a329f76e040047a35a1197defe342fab20"
|
||||||
integrity sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==
|
integrity sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==
|
||||||
|
|
||||||
|
use-query-params@^1.2.3:
|
||||||
|
version "1.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/use-query-params/-/use-query-params-1.2.3.tgz#306c31a0cbc714e8a3b4bd7e91a6a9aaccaa5e22"
|
||||||
|
integrity sha512-cdG0tgbzK+FzsV6DAt2CN8Saa3WpRnze7uC4Rdh7l15epSFq7egmcB/zuREvPNwO5Yk80nUpDZpiyHsoq50d8w==
|
||||||
|
dependencies:
|
||||||
|
serialize-query-params "^1.3.5"
|
||||||
|
|
||||||
use@^3.1.0:
|
use@^3.1.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||||
|
|||||||
Reference in New Issue
Block a user