TorrentList, Sidebar: use custom fancy scrollbars

This commit is contained in:
Jesse Chan
2020-10-28 23:01:04 +08:00
parent 6c1b44b4d1
commit f3e184a75f
8 changed files with 285 additions and 94 deletions
@@ -1,9 +1,54 @@
import {FixedSizeList} from 'react-window';
import {useWindowHeight} from '@react-hook/window-size';
import {OverlayScrollbarsComponent} from 'overlayscrollbars-react';
import {useMediaQuery} from '@react-hook/media-query';
import {useWindowSize} from '@react-hook/window-size';
import * as React from 'react';
import type {ListChildComponentProps} from 'react-window';
const Overflow = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(
(props: React.ComponentProps<'div'>, ref) => {
const {children, className, onScroll} = props;
const osRef = React.useRef<OverlayScrollbarsComponent>(null);
React.useEffect(() => {
const scrollbarRef = osRef.current;
if (scrollbarRef == null) {
return () => {
// do nothing.
};
}
const viewport = scrollbarRef.osInstance()?.getElements().viewport as HTMLDivElement;
const refCallback = ref as React.RefCallback<HTMLDivElement>;
refCallback(viewport);
if (onScroll) {
viewport.addEventListener('scroll', (e) => onScroll((e as unknown) as React.UIEvent<HTMLDivElement>), {
passive: true,
});
}
return () => {
if (onScroll) {
viewport.removeEventListener('scroll', (e) => onScroll((e as unknown) as React.UIEvent<HTMLDivElement>));
}
};
}, [onScroll]);
return (
<OverlayScrollbarsComponent
{...props}
options={{scrollbars: {autoHide: 'leave', clickScrolling: true}, className}}
ref={osRef}>
{children}
</OverlayScrollbarsComponent>
);
},
);
interface ListViewportProps {
className: string;
itemRenderer: React.FC<ListChildComponentProps>;
@@ -14,16 +59,18 @@ interface ListViewportProps {
const ListViewport = React.forwardRef<FixedSizeList, ListViewportProps>((props: ListViewportProps, ref) => {
const {className, itemRenderer, itemSize, listLength, outerRef} = props;
const windowHeight = useWindowHeight();
const [windowWidth, windowHeight] = useWindowSize();
const isDarkTheme = useMediaQuery('(prefers-color-scheme: dark)');
return (
<FixedSizeList
className={className}
className={`${className} ${isDarkTheme ? 'os-theme-light' : 'os-theme-dark'}`}
height={Math.max(itemSize * 30, windowHeight * 1.5)}
itemCount={listLength}
itemSize={itemSize}
width="100%"
innerElementType="ul"
outerElementType={windowWidth > 720 ? Overflow : undefined} // Don't use custom scrollbar on smaller screens
ref={ref}
outerRef={outerRef}>
{itemRenderer}
@@ -1,3 +1,6 @@
import {OverlayScrollbarsComponent} from 'overlayscrollbars-react';
import DiskUsage from './DiskUsage';
import FeedsButton from './FeedsButton';
import LogoutButton from './LogoutButton';
import NotificationsButton from './NotificationsButton';
@@ -9,11 +12,11 @@ import StatusFilters from './StatusFilters';
import TagFilters from './TagFilters';
import TrackerFilters from './TrackerFilters';
import TransferData from './TransferData';
import DiskUsage from './DiskUsage';
const Sidebar = () => {
return (
<div className="application__sidebar">
<OverlayScrollbarsComponent
options={{scrollbars: {autoHide: 'leave', autoHideDelay: 0}, className: 'application__sidebar os-theme-thin'}}>
<SidebarActions>
<SpeedLimitDropdown />
<SettingsButton />
@@ -27,7 +30,7 @@ const Sidebar = () => {
<TagFilters />
<TrackerFilters />
<DiskUsage />
</div>
</OverlayScrollbarsComponent>
);
};
@@ -8,6 +8,8 @@ import FloodActions from '../../actions/FloodActions';
import Sidebar from '../sidebar/Sidebar';
import TorrentList from '../torrent-list/TorrentList';
import 'overlayscrollbars/css/OverlayScrollbars.css';
const Alerts = lazy(() => import('../alerts/Alerts'));
const Modals = lazy(() => import('../modals/Modals'));
-27
View File
@@ -5,30 +5,3 @@ body {
ul {
list-style: none;
}
* {
scrollbar-width: thin;
}
::-webkit-scrollbar {
height: 6px;
width: 6px;
}
::-webkit-scrollbar-corner {
background: none;
}
::-webkit-scrollbar-track {
opacity: 0;
}
::-webkit-scrollbar-thumb {
border-radius: 4px;
background: #8d8d8d;
}
::-webkit-scrollbar-thumb:hover {
border-radius: 4px;
background: lighten(#8d8d8d, 10%);
}
+126 -44
View File
@@ -1,45 +1,127 @@
$scrollbar--thumb--background--inactive: rgba(#1a2f3d, 0.3);
$scrollbar--thumb--background--hover: rgba(#1a2f3d, 0.6);
$scrollbar--thumb--background--inverted--inactive: rgba(#e9eef2, 0.3);
$scrollbar--thumb--background--inverted--hover: rgba(#e9eef2, 0.6);
.scrollbars {
&__thumb {
background: $scrollbar--thumb--background--inactive;
border-radius: 10px;
cursor: pointer;
opacity: 0;
transition: background 0.25s, opacity 0.5s;
z-index: 2;
&:active {
opacity: 1;
}
&:hover,
&:active {
background: $scrollbar--thumb--background--hover;
}
&--surrogate {
display: block;
height: 100%;
width: 100%;
}
.is-inverted & {
background: $scrollbar--thumb--background--inverted--inactive;
&:hover,
&:active {
background: $scrollbar--thumb--background--inverted--hover;
}
}
}
&:hover {
.scrollbars__thumb {
opacity: 1;
}
}
* {
scrollbar-width: thin;
}
::-webkit-scrollbar {
height: 6px;
width: 6px;
}
::-webkit-scrollbar-corner {
background: none;
}
::-webkit-scrollbar-track {
opacity: 0;
}
::-webkit-scrollbar-thumb {
border-radius: 4px;
background: #8d8d8d;
}
::-webkit-scrollbar-thumb:hover {
border-radius: 4px;
background: lighten(#8d8d8d, 10%);
}
.os-theme-thin > .os-scrollbar-horizontal {
right: 14px;
height: 14px;
padding: 0px 6px;
}
.os-theme-thin > .os-scrollbar-vertical {
bottom: 14px;
width: 14px;
padding: 6px 0px;
}
.os-theme-thin.os-host-rtl > .os-scrollbar-horizontal {
left: 14px;
right: 0;
}
.os-theme-thin > .os-scrollbar-corner {
height: 14px;
width: 14px;
background-color: transparent;
}
.os-theme-thin > .os-scrollbar > .os-scrollbar-track {
background: transparent;
}
.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track:before,
.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track:before {
content: '';
display: block;
position: absolute;
background: rgba(255, 255, 255, 0.15);
}
.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track:before,
.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle:before {
left: 0;
right: 0;
height: 2px;
top: 50%;
margin-top: -1px;
}
.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track:before,
.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle:before {
top: 0;
bottom: 0;
width: 2px;
left: 50%;
margin-left: -1px;
}
.os-theme-thin > .os-scrollbar > .os-scrollbar-track > .os-scrollbar-handle:before {
content: '';
display: block;
position: absolute;
background: rgba(255, 255, 255, 0.5);
border-radius: 10px;
}
.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle:hover:before,
.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle.active:before {
height: 4px;
margin-top: -2px;
}
.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle:hover:before,
.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle.active:before {
width: 4px;
margin-left: -2px;
}
.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle:hover:before,
.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle:hover:before {
background: rgba(255, 255, 255, 0.7);
}
.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle.active:before,
.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle.active:before {
background: #fff;
}
.os-theme-thin > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle {
height: 100%;
min-width: 30px;
}
.os-theme-thin > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle {
width: 100%;
min-height: 30px;
}
.os-theme-thin.os-host-transition > .os-scrollbar-horizontal > .os-scrollbar-track > .os-scrollbar-handle:before {
transition: height 0.3s, margin-top 0.3s, background 0.2s;
}
.os-theme-thin.os-host-transition > .os-scrollbar-vertical > .os-scrollbar-track > .os-scrollbar-handle:before {
transition: width 0.3s, margin-left 0.3s, background 0.2s;
}
+10 -4
View File
@@ -375,10 +375,16 @@ declare const styles: {
readonly 'progress-bar__fill': string;
readonly 'progress-bar__fill__wrapper': string;
readonly 'candy-stripe': string;
readonly scrollbars__thumb: string;
readonly 'scrollbars__thumb--surrogate': string;
readonly 'is-inverted': string;
readonly scrollbars: string;
readonly 'os-theme-thin': string;
readonly 'os-scrollbar-horizontal': string;
readonly 'os-scrollbar-vertical': string;
readonly 'os-host-rtl': string;
readonly 'os-scrollbar-corner': string;
readonly 'os-scrollbar': string;
readonly 'os-scrollbar-track': string;
readonly 'os-scrollbar-handle': string;
readonly active: string;
readonly 'os-host-transition': string;
readonly search: string;
readonly textbox: string;
readonly 'is-in-use': string;
+87 -13
View File
@@ -23,6 +23,7 @@
"@babel/preset-react": "^7.10.4",
"@babel/preset-typescript": "^7.12.0",
"@formatjs/cli": "^2.13.2",
"@react-hook/media-query": "^1.1.1",
"@react-hook/window-size": "^3.0.7",
"@types/argon2-browser": "^1.12.0",
"@types/async": "^3.2.3",
@@ -46,6 +47,7 @@
"@types/morgan": "^1.9.1",
"@types/nedb": "^1.8.11",
"@types/node": "^12.12.67",
"@types/overlayscrollbars": "^1.12.0",
"@types/passport": "^1.0.4",
"@types/passport-jwt": "^3.0.3",
"@types/react": "^16.9.52",
@@ -121,6 +123,8 @@
"nedb": "^1.8.0",
"node-sass": "^4.13.0",
"optimize-css-assets-webpack-plugin": "^5.0.4",
"overlayscrollbars": "^1.13.0",
"overlayscrollbars-react": "^0.2.2",
"pascal-case": "^3.1.1",
"passport": "^0.4.1",
"passport-jwt": "^4.0.0",
@@ -2278,6 +2282,15 @@
"react": ">=16.8"
}
},
"node_modules/@react-hook/media-query": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@react-hook/media-query/-/media-query-1.1.1.tgz",
"integrity": "sha512-VM14wDOX5CW5Dn6b2lTiMd79BFMTut9AZj2+vIRT3LCKgMCYmdqruTtzDPSnIVDQdtxdPgtOzvU9oK20LopuOw==",
"dev": true,
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/@react-hook/passive-layout-effect": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz",
@@ -3085,6 +3098,12 @@
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
"dev": true
},
"node_modules/@types/overlayscrollbars": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@types/overlayscrollbars/-/overlayscrollbars-1.12.0.tgz",
"integrity": "sha512-h/pScHNKi4mb+TrJGDon8Yb06ujFG0mSg12wIO0sWMUF3dQIe2ExRRdNRviaNt9IjxIiOfnRr7FsQAdHwK4sMg==",
"dev": true
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@@ -15783,6 +15802,22 @@
"os-tmpdir": "^1.0.0"
}
},
"node_modules/overlayscrollbars": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.0.tgz",
"integrity": "sha512-p8oHrMeRAKxXDMPI/EBNITj/zTVHKNnAnM59Im+xnoZUlV07FyTg46wom2286jJlXGGfcPFG/ba5NUiCwWNd4w==",
"dev": true
},
"node_modules/overlayscrollbars-react": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/overlayscrollbars-react/-/overlayscrollbars-react-0.2.2.tgz",
"integrity": "sha512-sRJDaKIxD+No6ygMRaCxejuIH2CksSCUTfaDOzDhPt22lI3IPZq/+Ifw2RT4j+U7hgXLn9P5QqA00f2bsZVwPA==",
"dev": true,
"peerDependencies": {
"overlayscrollbars": "^1.10.0",
"react": "^16.4.0"
}
},
"node_modules/p-each-series": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz",
@@ -29589,13 +29624,22 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@react-hook/latest/-/latest-1.0.3.tgz",
"integrity": "sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==",
"dev": true
"dev": true,
"requires": {}
},
"@react-hook/media-query": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@react-hook/media-query/-/media-query-1.1.1.tgz",
"integrity": "sha512-VM14wDOX5CW5Dn6b2lTiMd79BFMTut9AZj2+vIRT3LCKgMCYmdqruTtzDPSnIVDQdtxdPgtOzvU9oK20LopuOw==",
"dev": true,
"requires": {}
},
"@react-hook/passive-layout-effect": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz",
"integrity": "sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==",
"dev": true
"dev": true,
"requires": {}
},
"@react-hook/throttle": {
"version": "2.2.0",
@@ -30364,6 +30408,12 @@
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
"dev": true
},
"@types/overlayscrollbars": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@types/overlayscrollbars/-/overlayscrollbars-1.12.0.tgz",
"integrity": "sha512-h/pScHNKi4mb+TrJGDon8Yb06ujFG0mSg12wIO0sWMUF3dQIe2ExRRdNRviaNt9IjxIiOfnRr7FsQAdHwK4sMg==",
"dev": true
},
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@@ -31028,7 +31078,8 @@
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz",
"integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==",
"dev": true
"dev": true,
"requires": {}
},
"acorn-walk": {
"version": "7.2.0",
@@ -31076,13 +31127,15 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
"integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
"dev": true
"dev": true,
"requires": {}
},
"ajv-keywords": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true
"dev": true,
"requires": {}
},
"alphanum-sort": {
"version": "1.0.2",
@@ -35247,7 +35300,8 @@
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz",
"integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==",
"dev": true
"dev": true,
"requires": {}
},
"eslint-scope": {
"version": "5.1.1",
@@ -37348,7 +37402,8 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.0.0.tgz",
"integrity": "sha512-aF2Cf/CkEZrI/vsu5WI/I+akFgdbwQHVE9YRZxATrhH4PVIe6a3BIjwjEcW+z+jP/hNh+YvM3lAAn1wJQ6opSg==",
"dev": true
"dev": true,
"requires": {}
},
"ieee754": {
"version": "1.2.1",
@@ -38333,7 +38388,8 @@
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz",
"integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==",
"dev": true
"dev": true,
"requires": {}
},
"jest-regex-util": {
"version": "26.0.0",
@@ -39330,7 +39386,8 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz",
"integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==",
"dev": true
"dev": true,
"requires": {}
},
"marked": {
"version": "0.8.2",
@@ -39775,7 +39832,8 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.0.1.tgz",
"integrity": "sha512-Ue8uGgT5iOjMyNf5ptoFW7BTvyLIwggzIkoFpwORrqf73TPqu47iLpz/DNvaba3v40kSsEpp050qYroMNuA1xw==",
"dev": true
"dev": true,
"requires": {}
},
"morgan": {
"version": "1.10.0",
@@ -40699,6 +40757,19 @@
"os-tmpdir": "^1.0.0"
}
},
"overlayscrollbars": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.0.tgz",
"integrity": "sha512-p8oHrMeRAKxXDMPI/EBNITj/zTVHKNnAnM59Im+xnoZUlV07FyTg46wom2286jJlXGGfcPFG/ba5NUiCwWNd4w==",
"dev": true
},
"overlayscrollbars-react": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/overlayscrollbars-react/-/overlayscrollbars-react-0.2.2.tgz",
"integrity": "sha512-sRJDaKIxD+No6ygMRaCxejuIH2CksSCUTfaDOzDhPt22lI3IPZq/+Ifw2RT4j+U7hgXLn9P5QqA00f2bsZVwPA==",
"dev": true,
"requires": {}
},
"p-each-series": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz",
@@ -42593,7 +42664,8 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
"integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
"dev": true
"dev": true,
"requires": {}
},
"postcss-modules-local-by-default": {
"version": "4.0.0",
@@ -48497,7 +48569,8 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/use-long-press/-/use-long-press-1.0.4.tgz",
"integrity": "sha512-Lw70Sfjut//hEsm/La/ik/5aL042No+4c5AzIb4FhCslLiuVdD8AN2nxRckeJMWBU/88xOwPZ+x+CD5Tjk6sPA==",
"dev": true
"dev": true,
"requires": {}
},
"utf8-byte-length": {
"version": "1.0.4",
@@ -50254,7 +50327,8 @@
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==",
"dev": true
"dev": true,
"requires": {}
},
"xml-name-validator": {
"version": "3.0.0",
+4
View File
@@ -62,6 +62,7 @@
"@babel/preset-react": "^7.10.4",
"@babel/preset-typescript": "^7.12.0",
"@formatjs/cli": "^2.13.2",
"@react-hook/media-query": "^1.1.1",
"@react-hook/window-size": "^3.0.7",
"@types/argon2-browser": "^1.12.0",
"@types/async": "^3.2.3",
@@ -85,6 +86,7 @@
"@types/morgan": "^1.9.1",
"@types/nedb": "^1.8.11",
"@types/node": "^12.12.67",
"@types/overlayscrollbars": "^1.12.0",
"@types/passport": "^1.0.4",
"@types/passport-jwt": "^3.0.3",
"@types/react": "^16.9.52",
@@ -160,6 +162,8 @@
"nedb": "^1.8.0",
"node-sass": "^4.13.0",
"optimize-css-assets-webpack-plugin": "^5.0.4",
"overlayscrollbars": "^1.13.0",
"overlayscrollbars-react": "^0.2.2",
"pascal-case": "^3.1.1",
"passport": "^0.4.1",
"passport-jwt": "^4.0.0",