Updated list servers action so that it tries to fetch servers from the servers.json file when no local servers are found

This commit is contained in:
Alejandro Celaya 2019-04-28 12:07:09 +02:00
parent 502c8a7e02
commit 20820c47d4
9 changed files with 74 additions and 48 deletions

View file

@ -3,7 +3,7 @@
"description": "A React-based progressive web application for shlink", "description": "A React-based progressive web application for shlink",
"version": "1.0.0", "version": "1.0.0",
"private": false, "private": false,
"homepage": "https://shlink.io", "homepage": "",
"scripts": { "scripts": {
"lint": "npm run lint:js && npm run lint:css", "lint": "npm run lint:js && npm run lint:css",
"lint:js": "eslint src test scripts config", "lint:js": "eslint src test scripts config",

View file

@ -18,18 +18,20 @@ export default class Home extends React.Component {
} }
render() { render() {
const servers = values(this.props.servers); const { servers: { list, loading } } = this.props;
const servers = values(list);
const hasServers = !isEmpty(servers); const hasServers = !isEmpty(servers);
return ( return (
<div className="home"> <div className="home">
<h1 className="home__title">Welcome to Shlink</h1> <h1 className="home__title">Welcome to Shlink</h1>
<h5 className="home__intro"> <h5 className="home__intro">
{hasServers && <span>Please, select a server.</span>} {!loading && hasServers && <span>Please, select a server.</span>}
{!hasServers && <span>Please, <Link to="/server/create">add a server</Link>.</span>} {!loading && !hasServers && <span>Please, <Link to="/server/create">add a server</Link>.</span>}
{loading && <span>Trying to load servers....</span>}
</h5> </h5>
{hasServers && ( {!loading && hasServers && (
<ListGroup className="home__servers-list"> <ListGroup className="home__servers-list">
{servers.map(({ name, id }) => ( {servers.map(({ name, id }) => (
<ListGroupItem <ListGroupItem

View file

@ -14,7 +14,12 @@ const ServersDropdown = (serversExporter) => class ServersDropdown extends React
}; };
renderServers = () => { renderServers = () => {
const { servers, selectedServer, selectServer } = this.props; const { servers: { list, loading }, selectedServer, selectServer } = this.props;
const servers = values(list);
if (loading) {
return <DropdownItem disabled><i>Trying to load servers...</i></DropdownItem>;
}
if (isEmpty(servers)) { if (isEmpty(servers)) {
return <DropdownItem disabled><i>Add a server first...</i></DropdownItem>; return <DropdownItem disabled><i>Add a server first...</i></DropdownItem>;
@ -22,7 +27,7 @@ const ServersDropdown = (serversExporter) => class ServersDropdown extends React
return ( return (
<React.Fragment> <React.Fragment>
{values(servers).map(({ name, id }) => ( {servers.map(({ name, id }) => (
<DropdownItem <DropdownItem
key={id} key={id}
tag={Link} tag={Link}
@ -46,18 +51,14 @@ const ServersDropdown = (serversExporter) => class ServersDropdown extends React
); );
}; };
componentDidMount() { componentDidMount = this.props.listServers;
this.props.listServers();
}
render() { render = () => (
return ( <UncontrolledDropdown nav inNavbar>
<UncontrolledDropdown nav inNavbar> <DropdownToggle nav caret>Servers</DropdownToggle>
<DropdownToggle nav caret>Servers</DropdownToggle> <DropdownMenu right>{this.renderServers()}</DropdownMenu>
<DropdownMenu right>{this.renderServers()}</DropdownMenu> </UncontrolledDropdown>
</UncontrolledDropdown> );
);
}
}; };
export default ServersDropdown; export default ServersDropdown;

View file

@ -1,7 +1,5 @@
import React from 'react'; import React from 'react';
import { UncontrolledTooltip } from 'reactstrap'; import { UncontrolledTooltip } from 'reactstrap';
import { assoc, map } from 'ramda';
import { v4 as uuid } from 'uuid';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const ImportServersBtn = (serversImporter) => class ImportServersBtn extends React.Component { const ImportServersBtn = (serversImporter) => class ImportServersBtn extends React.Component {
@ -22,10 +20,8 @@ const ImportServersBtn = (serversImporter) => class ImportServersBtn extends Rea
render() { render() {
const { importServersFromFile } = serversImporter; const { importServersFromFile } = serversImporter;
const { onImport, createServers } = this.props; const { onImport, createServers } = this.props;
const assocId = (server) => assoc('id', uuid(), server);
const onChange = ({ target }) => const onChange = ({ target }) =>
importServersFromFile(target.files[0]) importServersFromFile(target.files[0])
.then(map(assocId))
.then(createServers) .then(createServers)
.then(onImport) .then(onImport)
.then(() => { .then(() => {

View file

@ -10,10 +10,10 @@ const initialState = null;
export const resetSelectedServer = createAction(RESET_SELECTED_SERVER); export const resetSelectedServer = createAction(RESET_SELECTED_SERVER);
export const selectServer = (serversService) => (serverId) => (dispatch) => { export const selectServer = ({ findServerById }) => (serverId) => (dispatch) => {
dispatch(resetShortUrlParams()); dispatch(resetShortUrlParams());
const selectedServer = serversService.findServerById(serverId); const selectedServer = findServerById(serverId);
dispatch({ dispatch({
type: SELECT_SERVER, type: SELECT_SERVER,

View file

@ -1,16 +1,51 @@
import { createAction, handleActions } from 'redux-actions'; import { handleActions } from 'redux-actions';
import { pipe } from 'ramda'; import { pipe, isEmpty, assoc, map } from 'ramda';
import { v4 as uuid } from 'uuid';
import { homepage } from '../../../package.json';
/* eslint-disable padding-line-between-statements */
export const FETCH_SERVERS_START = 'shlink/servers/FETCH_SERVERS_START';
export const FETCH_SERVERS = 'shlink/servers/FETCH_SERVERS'; export const FETCH_SERVERS = 'shlink/servers/FETCH_SERVERS';
/* eslint-enable padding-line-between-statements */
export const listServers = ({ listServers }) => createAction(FETCH_SERVERS, () => listServers()); const initialState = {
list: {},
loading: false,
};
export const createServer = ({ createServer }, listServers) => pipe(createServer, listServers); const assocId = (server) => assoc('id', uuid(), server);
export const deleteServer = ({ deleteServer }, listServers) => pipe(deleteServer, listServers);
export const createServers = ({ createServers }, listServers) => pipe(createServers, listServers);
export default handleActions({ export default handleActions({
[FETCH_SERVERS]: (state, { payload }) => payload, [FETCH_SERVERS_START]: (state) => ({ ...state, loading: true }),
}, {}); [FETCH_SERVERS]: (state, { list }) => ({ list, loading: false }),
}, initialState);
export const listServers = ({ listServers, createServers }, { get }) => () => async (dispatch) => {
dispatch({ type: FETCH_SERVERS_START });
// Fetch list from local storage.
const localList = listServers();
if (!isEmpty(localList)) {
dispatch({ type: FETCH_SERVERS, list: localList });
return;
}
// If local list is empty, try to fetch it remotely, calculate IDs for every server, and use it
const { data: remoteList } = await get(`${homepage}/servers.json`);
const listWithIds = map(assocId, remoteList);
createServers(listWithIds);
dispatch({ type: FETCH_SERVERS, list: listWithIds });
};
export const createServer = ({ createServer }, listServersAction) => pipe(createServer, listServersAction);
export const deleteServer = ({ deleteServer }, listServersAction) => pipe(deleteServer, listServersAction);
export const createServers = ({ createServers }, listServersAction) => pipe(
map(assocId),
createServers,
listServersAction
);

View file

@ -1,9 +1,3 @@
import PropTypes from 'prop-types';
export const serversImporterType = PropTypes.shape({
importServersFromFile: PropTypes.func,
});
export default class ServersImporter { export default class ServersImporter {
constructor(csvjson) { constructor(csvjson) {
this.csvjson = csvjson; this.csvjson = csvjson;

View file

@ -1,10 +1,11 @@
import { assoc, dissoc, reduce } from 'ramda'; import { assoc, curry, dissoc, reduce } from 'ramda';
const SERVERS_STORAGE_KEY = 'servers'; const SERVERS_STORAGE_KEY = 'servers';
export default class ServersService { export default class ServersService {
constructor(storage) { constructor(storage) {
this.storage = storage; this.storage = storage;
this.setServers = curry(this.storage.set)(SERVERS_STORAGE_KEY);
} }
listServers = () => this.storage.get(SERVERS_STORAGE_KEY) || {}; listServers = () => this.storage.get(SERVERS_STORAGE_KEY) || {};
@ -20,12 +21,9 @@ export default class ServersService {
servers servers
); );
this.storage.set(SERVERS_STORAGE_KEY, allServers); this.setServers(allServers);
}; };
deleteServer = (server) => deleteServer = ({ id }) =>
this.storage.set( this.setServers(dissoc(id, this.listServers()));
SERVERS_STORAGE_KEY,
dissoc(server.id, this.listServers())
);
} }

View file

@ -38,7 +38,7 @@ const provideServices = (bottle, connect, withRouter) => {
bottle.serviceFactory('createServer', createServer, 'ServersService', 'listServers'); bottle.serviceFactory('createServer', createServer, 'ServersService', 'listServers');
bottle.serviceFactory('createServers', createServers, 'ServersService', 'listServers'); bottle.serviceFactory('createServers', createServers, 'ServersService', 'listServers');
bottle.serviceFactory('deleteServer', deleteServer, 'ServersService', 'listServers'); bottle.serviceFactory('deleteServer', deleteServer, 'ServersService', 'listServers');
bottle.serviceFactory('listServers', listServers, 'ServersService'); bottle.serviceFactory('listServers', listServers, 'ServersService', 'axios');
bottle.serviceFactory('resetSelectedServer', () => resetSelectedServer); bottle.serviceFactory('resetSelectedServer', () => resetSelectedServer);
}; };