1
0
mirror of https://github.com/verdaccio/verdaccio.git synced 2025-02-17 03:19:36 +01:00

feat: migrate react router from hash to history api #1013

This commit is contained in:
Juan Picado @jotadeveloper 2019-01-20 22:16:54 +01:00
parent 8c06f968f9
commit 1a456fb8a6
No known key found for this signature in database
GPG Key ID: 18AC54485952D158
9 changed files with 67 additions and 40 deletions

@ -53,13 +53,7 @@ module.exports = function(config, auth, storage) {
}); });
}); });
router.get('/-/verdaccio/logo', function(req, res) { function renderHTML(req, res) {
const installPath = _.get(config, 'url_prefix', '');
res.send(_.get(config, 'web.logo') || spliceURL(installPath, '/-/static/logo.png'));
});
router.get('/', function(req, res) {
const base = combineBaseUrl(getWebProtocol(req.get(HEADERS.FORWARDED_PROTO), req.protocol), req.get('host'), config.url_prefix); const base = combineBaseUrl(getWebProtocol(req.get(HEADERS.FORWARDED_PROTO), req.protocol), req.get('host'), config.url_prefix);
const webPage = template const webPage = template
@ -70,6 +64,20 @@ module.exports = function(config, auth, storage) {
res.setHeader('Content-Type', 'text/html'); res.setHeader('Content-Type', 'text/html');
res.send(webPage); res.send(webPage);
}
router.get('/-/web/:pkg', function(req, res) {
renderHTML(req, res);
});
router.get('/-/verdaccio/logo', function(req, res) {
const installPath = _.get(config, 'url_prefix', '');
res.send(_.get(config, 'web.logo') || spliceURL(installPath, '/-/static/logo.png'));
});
router.get('/', function(req, res) {
renderHTML(req, res);
}); });
return router; return router;

@ -5,16 +5,16 @@ import storage from './utils/storage';
import logo from './utils/logo'; import logo from './utils/logo';
import { makeLogin, isTokenExpire } from './utils/login'; import { makeLogin, isTokenExpire } from './utils/login';
import Footer from './components/Footer';
import Loading from './components/Loading'; import Loading from './components/Loading';
import LoginModal from './components/Login'; import LoginModal from './components/Login';
import Header from './components/Header'; import Header from './components/Header';
import { Container, Content } from './components/Layout'; import { Container, Content } from './components/Layout';
import Route from './router'; import RouterApp from './router';
import API from './utils/api'; import API from './utils/api';
import './styles/typeface-roboto.scss'; import './styles/typeface-roboto.scss';
import './styles/main.scss'; import './styles/main.scss';
import 'normalize.css'; import 'normalize.css';
import Footer from './components/Footer';
export const AppContext = React.createContext(); export const AppContext = React.createContext();
@ -143,15 +143,14 @@ export default class App extends Component {
} }
render() { render() {
const { isLoading, isUserLoggedIn, packages } = this.state; const { isLoading, isUserLoggedIn, packages, logoUrl, user, scope } = this.state;
return ( return (
<Container isLoading={isLoading}> <Container isLoading={isLoading}>
{isLoading ? ( {isLoading ? (
<Loading /> <Loading />
) : ( ) : (
<Fragment> <Fragment>
<AppContextProvider value={{isUserLoggedIn, packages}}> <AppContextProvider value={{isUserLoggedIn, packages, logoUrl, user, scope}}>
{this.renderHeader()}
{this.renderContent()} {this.renderContent()}
</AppContextProvider> </AppContextProvider>
</Fragment> </Fragment>
@ -178,7 +177,11 @@ export default class App extends Component {
return ( return (
<Fragment> <Fragment>
<Content> <Content>
<Route></Route> <RouterApp
onLogout={this.handleLogout}
onToggleLoginModal={this.handleToggleLoginModal}>
{this.renderHeader()}
</RouterApp>
</Content> </Content>
<Footer /> <Footer />
</Fragment> </Fragment>

@ -34,14 +34,14 @@ class DepDetail extends Component<any, any> {
const { onLoading, history } = this.props; const { onLoading, history } = this.props;
onLoading(); onLoading();
history.push(`/version/${name}`); history.push(`/-/web/version/${name}`);
}; };
} }
const WrappDepDetail = withRouter(DepDetail); const WrappDepDetail = withRouter(DepDetail);
class DependencyBlock extends Component<any, any> { class DependencyBlock extends Component<any, any> {
renderTags = (deps: object, enableLoading: boolean) => renderTags = (deps: any, enableLoading: boolean) =>
deps.map(dep => { deps.map(dep => {
const [name, version] = dep; const [name, version] = dep;

@ -5,6 +5,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import type { Node } from 'react'; import type { Node } from 'react';
import { Link } from 'react-router-dom';
import Button from '@material-ui/core/Button/index'; import Button from '@material-ui/core/Button/index';
import IconButton from '@material-ui/core/IconButton/index'; import IconButton from '@material-ui/core/IconButton/index';
@ -17,7 +18,7 @@ import AccountCircle from '@material-ui/icons/AccountCircle';
import { default as IconSearch } from '@material-ui/icons/Search'; import { default as IconSearch } from '@material-ui/icons/Search';
import { getRegistryURL } from '../../utils/url'; import { getRegistryURL } from '../../utils/url';
import Link from '../Link'; import ExternalLink from '../Link';
import Logo from '../Logo'; import Logo from '../Logo';
import RegistryInfoDialog from '../RegistryInfoDialog'; import RegistryInfoDialog from '../RegistryInfoDialog';
import Label from '../Label'; import Label from '../Label';
@ -128,7 +129,7 @@ class Header extends Component<IProps, IState> {
switch (type) { switch (type) {
case 'help': case 'help':
content = ( content = (
<IconButton blank={true} color={'inherit'} component={Link} to={'https://verdaccio.org/docs/en/installation'}> <IconButton blank={true} color={'inherit'} component={ExternalLink} to={'https://verdaccio.org/docs/en/installation'}>
<Help /> <Help />
</IconButton> </IconButton>
); );

@ -80,7 +80,7 @@ const Package = ({ name: label, version, time, author: { name, avatar }, descrip
); );
return ( return (
<WrapperLink className={'package'} to={`version/${label}`}> <WrapperLink className={'package'} to={`/-/web/version/${label}`}>
<Header> <Header>
{renderMainInfo()} {renderMainInfo()}
<Overview> <Overview>

@ -5,6 +5,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import type { Node } from 'react'; import type { Node } from 'react';
import { withRouter } from 'react-router-dom';
import { default as IconSearch } from '@material-ui/icons/Search'; import { default as IconSearch } from '@material-ui/icons/Search';
import InputAdornment from '@material-ui/core/InputAdornment'; import InputAdornment from '@material-ui/core/InputAdornment';
@ -13,7 +14,6 @@ import debounce from 'lodash/debounce';
import API from '../../utils/api'; import API from '../../utils/api';
import AutoComplete from '../AutoComplete'; import AutoComplete from '../AutoComplete';
import colors from '../../utils/styles/colors'; import colors from '../../utils/styles/colors';
import { getDetailPageURL } from '../../utils/url';
import { IProps, IState } from './types'; import { IProps, IState } from './types';
import type { cancelAllSearchRequests, handlePackagesClearRequested, handleSearch, handleClickSearch, handleFetchPackages, onBlur } from './types'; import type { cancelAllSearchRequests, handlePackagesClearRequested, handleSearch, handleClickSearch, handleFetchPackages, onBlur } from './types';
@ -92,13 +92,15 @@ class Search extends Component<IProps, IState> {
* When an user select any package by clicking or pressing return key. * When an user select any package by clicking or pressing return key.
*/ */
handleClickSearch: handleClickSearch = (event, { suggestionValue, method }) => { handleClickSearch: handleClickSearch = (event, { suggestionValue, method }) => {
const { history } = this.props;
// stops event bubbling // stops event bubbling
event.stopPropagation(); event.stopPropagation();
switch (method) { switch (method) {
case 'click': case 'click':
case 'enter': case 'enter':
this.setState({ search: '' }); this.setState({ search: '' });
window.location.assign(getDetailPageURL(suggestionValue)); // $FlowFixMe
history.push(`/-/web/version/${suggestionValue}`);
break; break;
} }
}; };
@ -181,4 +183,4 @@ class Search extends Component<IProps, IState> {
} }
} }
export default Search; export default withRouter(Search);

@ -3,7 +3,9 @@
* @flow * @flow
*/ */
export interface IProps {} export interface IProps {
history?: any;
}
export interface IState { export interface IState {
search: string; search: string;

@ -2,9 +2,9 @@
* @prettier * @prettier
*/ */
import { createHashHistory } from 'history'; import { createBrowserHistory } from 'history';
const history = createHashHistory(); const history = createBrowserHistory();
// Listen for changes to the current location. // Listen for changes to the current location.
history.listen((location, action) => { history.listen((location, action) => {

@ -3,39 +3,50 @@
* @flow * @flow
*/ */
import React, { Component } from 'react'; /* eslint react/jsx-max-depth:0 */
import React, { Component, Fragment } from 'react';
import { Router, Route, Switch } from 'react-router-dom'; import { Router, Route, Switch } from 'react-router-dom';
import { AppContextConsumer } from './app'; import { AppContextConsumer } from './app';
import { asyncComponent } from './utils/asyncComponent'; import { asyncComponent } from './utils/asyncComponent';
import history from './history'; import history from './history';
import Header from './components/Header';
const DetailPackage = asyncComponent(() => import('./pages/detail')); const DetailPackage = asyncComponent(() => import('./pages/detail'));
const VersionPackage = asyncComponent(() => import('./pages/version')); const VersionPackage = asyncComponent(() => import('./pages/version'));
const HomePage = asyncComponent(() => import('./pages/home')); const HomePage = asyncComponent(() => import('./pages/home'));
interface IProps { class RouterApp extends Component<any, any> {
isUserLoggedIn: boolean;
packages: Array<Object>;
}
interface IState {}
class RouterApp extends Component<IProps, IState> {
render() { render() {
return ( return (
<Router history={history}> <Router history={history}>
<Switch> <Fragment>
<Route exact={true} path={'/'} render={this.renderHomePage} /> {this.renderHeader()}
<Route exact={true} path={'/detail/@:scope/:package'} render={this.renderDetailPage} /> <Switch>
<Route exact={true} path={'/detail/:package'} render={this.renderDetailPage} /> <Route exact={true} path={'/'} render={this.renderHomePage} />
<Route exact={true} path={'/version/@:scope/:package'} render={this.renderVersionPage} /> <Route exact={true} path={'/-/web/detail/@:scope/:package'} render={this.renderDetailPage} />
<Route exact={true} path={'/version/:package'} render={this.renderVersionPage} /> <Route exact={true} path={'/-/web/detail/:package'} render={this.renderDetailPage} />
</Switch> <Route exact={true} path={'/-/web/version/@:scope/:package'} render={this.renderVersionPage} />
<Route exact={true} path={'/-/web/version/:package'} render={this.renderVersionPage} />
</Switch>
</Fragment>
</Router> </Router>
); );
} }
renderHeader = () => {
const { onLogout, onToggleLoginModal } = this.props;
return (
<AppContextConsumer>
{function renderConsumerVersionPage({ logoUrl, scope, user }) {
return <Header logo={logoUrl} onLogout={onLogout} onToggleLoginModal={onToggleLoginModal} scope={scope} username={user.username} />;
}}
</AppContextConsumer>
);
};
renderHomePage = () => { renderHomePage = () => {
return ( return (
<AppContextConsumer> <AppContextConsumer>