Skip to content

Commit

Permalink
feat(Walk): Standardize directory listing to GenericList
Browse files Browse the repository at this point in the history
  • Loading branch information
danactive committed Jun 13, 2020
1 parent dfd3ec9 commit 1450df2
Show file tree
Hide file tree
Showing 14 changed files with 148 additions and 143 deletions.
11 changes: 4 additions & 7 deletions ui/app/components/GenericList/index.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
/**
*
* GenericList
*
*/

import React from 'react';

import List from '../List';
import ListItem from '../ListItem';
import LoadingIndicator from '../LoadingIndicator';

function GenericList({
loading, error, items, component,
component,
items,
loading,
error,
}) {
if (loading) {
return <List component={LoadingIndicator} />;
Expand Down
5 changes: 3 additions & 2 deletions ui/app/components/List/tests/index.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import List from '..';

describe('<List />', () => {
test('should render the passed component if no items are passed', () => {
const component = () => <li>test</li>; // eslint-disable-line react/prop-types
const component = () => <li id="expected id">test</li>;
const { container } = render(<List component={component} />);
expect(container.querySelector('li')).not.toBeNull();
expect(container.querySelector('li').id).toEqual('expected id');
});

test('should pass all items props to rendered component', () => {
const items = [{ id: 1, name: 'Hello' }, { id: 2, name: 'World' }];

const component = ({ item }) => <li>{item.name}</li>; // eslint-disable-line react/prop-types
const component = ({ item, key }) => <li title={key}>{item.name}</li>;

const { container, getByText } = render(
<List items={items} component={component} />,
Expand Down
25 changes: 5 additions & 20 deletions ui/app/containers/AdminLandingPage/index.jsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,26 @@
import { push } from 'connected-react-router';
import React from 'react';
import { Helmet } from 'react-helmet';
import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { compose } from 'redux';

import A from '../../components/A';
import messages from './messages';

export function AdminLandingPage({
navToResize,
navToWalk,
}) {
export function AdminLandingPage() {
return (
<div>
<Helmet>
<title>Admin</title>
<title>Walk - Admin</title>
<meta name="description" content="Admin" />
</Helmet>
<h1>
<FormattedMessage {...messages.header} />
</h1>
<ul>
<li><A onClick={navToWalk}>Walk</A></li>
<li><A onClick={navToResize}>Resize</A></li>
<li><A href="/admin/walk">Walk</A></li>
<li><A href="/admin/resize">Resize</A></li>
</ul>
</div>
);
}

const mapDispatchToProps = (dispatch) => ({
navToWalk: () => dispatch(push('/admin/walk')),
navToResize: () => dispatch(push('/admin/resize')),
});

const withConnect = connect(null, mapDispatchToProps);

export default compose(
withConnect,
)(AdminLandingPage);
export default AdminLandingPage;
32 changes: 32 additions & 0 deletions ui/app/containers/Walk/Contents.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';

import A from '../../components/A';
import ListItem from '../../components/ListItem';
import Placeholder from './Placeholder';

import walkUtils from './util';

const { areImages } = walkUtils;

function Contents({ item: file }) {
const key = `contents-${file.id.replace(/\//, '-') || 'missingId'}`;
if (areImages(file)) {
return <Placeholder file={file} key={key} />;
}

if (file.mediumType === 'folder') {
const Link = (
<A href={`?path=${file.path}`}>
{file.filename}
</A>
);

return (<ListItem item={Link} />);
}

return (
<ListItem item={file.filename} />
);
}

export default Contents;
File renamed without changes.
4 changes: 2 additions & 2 deletions ui/app/containers/Walk/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
LIST_DIRECTORY_FAILURE,
} from './constants';

function getFilesByPath(path) {
function listDirectory(path) {
return {
type: LIST_DIRECTORY_REQUEST,
path,
Expand All @@ -26,7 +26,7 @@ function listingFailure(error) {
}

export default {
getFilesByPath,
listDirectory,
listingSuccess,
listingFailure,
};
40 changes: 0 additions & 40 deletions ui/app/containers/Walk/contents.jsx

This file was deleted.

58 changes: 27 additions & 31 deletions ui/app/containers/Walk/index.jsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,62 @@
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
import { createStructuredSelector } from 'reselect';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { createStructuredSelector } from 'reselect';

import { useInjectSaga } from '../../utils/injectSaga';
import { useInjectReducer } from '../../utils/injectReducer';
import actions from './actions';
import {
makeSelectFiles,
makeSelectPath,
} from './selectors';
import reducer from './reducer';
import saga from './saga';
import { makeSelectFiles, makeSelectPath } from './selectors';
import { useInjectSaga } from '../../utils/injectSaga';
import { useInjectReducer } from '../../utils/injectReducer';

import Contents from './contents';

import walkUtils from './util';

const { areImages } = walkUtils;
import Contents from './Contents';
import GenericList from '../../components/GenericList';

function parseQueryString(find, from) {
if (!find || !from) return '';
return RegExp(`[?&]${find}(=([^&#]*)|&|#|$)`).exec(from)[2];
}

function Walk({
getFilesByPath,
doListDirectory,
files,
location: { search: querystring },
path: pathProp,
statePath,
}) {
useInjectReducer({ key: 'walk', reducer });
useInjectSaga({ key: 'walk', saga });

const qsPath = parseQueryString('path', querystring);
const path = qsPath || pathProp;
const path = qsPath || statePath;

useEffect(() => {
getFilesByPath(path);
}, []);

const hasImage = files.some(areImages);
doListDirectory(path);
}, [doListDirectory, path]);

return (
<div>
<Helmet>
<title>Walk</title>
<meta name="description" content="Description of Walk" />
</Helmet>
<Contents files={files} showControls={hasImage} />
</div>
);
return [
<Helmet key="walk-Helmet">
<title>Walk</title>
<meta name="description" content="Description of Walk" />
</Helmet>,
<GenericList
key="walk-GenericList"
component={Contents}
items={files.map((f) => ({ id: f.path, ...f }))}
loading={!files || files.length === 0}
error={false}
/>,
];
}

const mapStateToProps = createStructuredSelector({
files: makeSelectFiles(),
path: makeSelectPath(),
statePath: makeSelectPath(),
});

const mapDispatchToProps = (dispatch) => ({
getFilesByPath: (path) => dispatch(actions.getFilesByPath(path)),
doListDirectory: (path) => dispatch(actions.listDirectory(path)),
});

const withConnect = connect(mapStateToProps, mapDispatchToProps);
Expand Down
1 change: 0 additions & 1 deletion ui/app/containers/Walk/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { LIST_DIRECTORY_REQUEST, LIST_DIRECTORY_SUCCESS } from './constants';
export const initialState = {
listing: {
files: [],
path: 'galleries',
},
};

Expand Down
1 change: 1 addition & 0 deletions ui/app/containers/Walk/rename.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ function onClick() {
body: JSON.stringify(data),
};

// TODO replace with request and fetchWithTimeout
return fetch('/admin/rename', options)
.then((response) => response.json())
.then(output)
Expand Down
16 changes: 13 additions & 3 deletions ui/app/containers/Walk/saga.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,25 @@ import {
takeLatest,
} from 'redux-saga/effects';

import request, { querystring } from '../../utils/request';
import { apiPort as port } from '../../../../config.json';
import actions from './actions';
import { apiPort as port } from '../../../../config.json';
import { LIST_DIRECTORY_REQUEST } from './constants';
import request, { querystring } from '../../utils/request';

function getBaseUrl(path) {
const baseUrl = `http://localhost:${port}/admin/walk-path`;

if (path) {
return querystring(baseUrl, { path });
}

return baseUrl;
}

// File system directory request/response handler
export function* requestDirectoryListing({ path }) {
try {
const url = querystring(`http://localhost:${port}/admin/walk-path`, { path });
const url = getBaseUrl(path);
const listing = yield call(request, url);
yield put(actions.listingSuccess(listing));
} catch (error) {
Expand Down
4 changes: 2 additions & 2 deletions ui/app/containers/Walk/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ const selectWalkDomain = (state) => state.walk || initialState;

const makeSelectFiles = () => createSelector(
selectWalkDomain,
(substate) => substate.listing.files,
(pageState) => pageState.listing.files,
);

const makeSelectPath = () => createSelector(
selectWalkDomain,
(substate) => substate.listing.path,
(pageState) => pageState.listing.path,
);

export default selectWalkDomain;
Expand Down
20 changes: 15 additions & 5 deletions ui/app/utils/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,22 @@ export function querystring(rawUrl, params) {
}

// *********** HISTORY CUSTOM not React Boilerplate
function fetchWithTimeout(url, options, timeout = 1700) {
function fetchWithTimeout(url, options, wait = 1700) {
let timerId;
const timeout = new Promise((_, reject) => {
timerId = setTimeout(() => {
clearTimeout(timerId);
reject(new Error(`XHR timeout (${timeout}) to ${url}`))
}, wait)
})

return Promise.race([
fetch(url, options),
new Promise((_, reject) =>
setTimeout(() => reject(new Error(`HTTP request timeout reached at ${timeout}`)), timeout)
)
fetch(url, options)
.then((response) => {
clearTimeout(timerId);
return response;
}),
timeout,
]);
}

Expand Down
Loading

0 comments on commit 1450df2

Please sign in to comment.