Skip to content

Commit

Permalink
feat(layer): fetch layer details
Browse files Browse the repository at this point in the history
  • Loading branch information
pwambach committed Oct 21, 2019
1 parent e6a9df4 commit 7b62d20
Show file tree
Hide file tree
Showing 19 changed files with 239 additions and 54 deletions.
43 changes: 43 additions & 0 deletions src/scripts/actions/fetch-layer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {Dispatch} from 'redux';

import fetchLayerApi from '../api/fetch-layer';

import {Layer} from '../types/layer';

export const FETCH_LAYER_SUCCESS = 'FETCH_LAYER_SUCCESS';
export const FETCH_LAYER_ERROR = 'FETCH_LAYER_ERROR';

export interface FetchLayerSuccessAction {
type: typeof FETCH_LAYER_SUCCESS;
id: string;
layer: Layer;
}

interface FetchLayerErrorAction {
type: typeof FETCH_LAYER_ERROR;
message: string;
}

export type FetchLayerActions = FetchLayerSuccessAction | FetchLayerErrorAction;

function fetchLayerSuccessAction(id: string, layer: Layer) {
return {
type: FETCH_LAYER_SUCCESS,
id,
layer
};
}

function fetchLayerErrorAction(message: string) {
return {
type: FETCH_LAYER_ERROR,
message
};
}

const fetchLayer = (id: string) => (dispatch: Dispatch) =>
fetchLayerApi(id)
.then(layer => dispatch(fetchLayerSuccessAction(id, layer)))
.catch(error => dispatch(fetchLayerErrorAction(error.message)));

export default fetchLayer;
6 changes: 3 additions & 3 deletions src/scripts/actions/fetch-layers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import fetchLayersApi from '../api/fetch-layers';
import {languageSelector} from '../reducers/language';

import {State} from '../reducers/index';
import {Layer} from '../types/layer';
import {LayerList} from '../types/layer-list';

export const FETCH_LAYERS_SUCCESS = 'FETCH_LAYERS_SUCCESS';
export const FETCH_LAYERS_ERROR = 'FETCH_LAYERS_ERROR';

interface FetchLayersSuccessAction {
type: typeof FETCH_LAYERS_SUCCESS;
layers: Layer[];
layers: LayerList;
}

interface FetchLayersErrorAction {
Expand All @@ -23,7 +23,7 @@ export type FetchLayersActions =
| FetchLayersSuccessAction
| FetchLayersErrorAction;

function fetchLayersSuccessAction(layers: Layer[]) {
function fetchLayersSuccessAction(layers: LayerList) {
return {
type: FETCH_LAYERS_SUCCESS,
layers
Expand Down
7 changes: 7 additions & 0 deletions src/scripts/api/fetch-layer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import config from '../config/main';
import {replaceUrlPlaceholders} from '../libs/replace-url-placeholders';

export default function fetchLayer(id: string) {
const url = replaceUrlPlaceholders(config.api.layer, {id});
return fetch(url).then(res => res.json());
}
6 changes: 5 additions & 1 deletion src/scripts/api/fetch-layers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import config from '../config/main';
import {replaceUrlPlaceholders} from '../libs/replace-url-placeholders';

import {Language} from '../types/language';

export default function fetchLayers(language: Language) {
const url = `${config.api.layers}-${language.toLowerCase()}.json`;
const url = replaceUrlPlaceholders(config.api.layers, {
lang: language.toLowerCase()
});

return fetch(url).then(res => res.json());
}
2 changes: 2 additions & 0 deletions src/scripts/components/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import ShowcaseSelector from '../showcase-selector/showcase-selector';
import StoriesSelector from '../stories-selector/stories-selector';
import StoriesButton from '../stories-button/stories-button';
import UrlSync from '../url-sync/url-sync';
import LayerLoader from '../layer-loader/layer-loader';

import translations from '../../i18n';
import styles from './app.styl';
Expand Down Expand Up @@ -68,6 +69,7 @@ const TranslatedApp: FunctionComponent = () => {
</IntlProvider>

<UrlSync />
<LayerLoader />
</Router>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/scripts/components/globes/globes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import React, {
} from 'react';
import {useSelector, useDispatch} from 'react-redux';

import {selectedLayersSelector} from '../../reducers/selected-layers';
import {selectedLayersSelector} from '../../reducers/layers/selected';
import {globeViewSelector} from '../../reducers/globe/view';
import {projectionSelector} from '../../reducers/globe/projection';
import setGlobeViewAction from '../../actions/set-globe-view';
Expand Down
4 changes: 2 additions & 2 deletions src/scripts/components/layer-list/layer-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import React, {FunctionComponent, MouseEvent} from 'react';
import cx from 'classnames';
import styles from './layer-list.styl';

import {Layer} from '../../types/layer';
import {LayerList as LayerListType} from '../../types/layer-list';

interface Props {
layers: Layer[];
layers: LayerListType;
selected: string | null;
onSelect: (id: string) => void;
}
Expand Down
37 changes: 37 additions & 0 deletions src/scripts/components/layer-loader/layer-loader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {FunctionComponent, useEffect} from 'react';
import {useSelector, useDispatch} from 'react-redux';

import fetchLayers from '../../actions/fetch-layers';
import fetchLayerAction from '../../actions/fetch-layer';
import {selectedLayersSelector} from '../../reducers/layers/selected';
import {detailedLayersSelector} from '../../reducers/layers/details';

/**
* Handles loading of layer list and layer details data
*/
const LayerLoader: FunctionComponent = () => {
const dispatch = useDispatch();
const selectedLayers = useSelector(selectedLayersSelector);
const detailedLayers = useSelector(detailedLayersSelector);
const {main, compare} = selectedLayers;

// load layer list on mount
useEffect(() => {
dispatch(fetchLayers());
}, [dispatch]);

// fetch layer if it is selected and not already downloaded
useEffect(() => {
if (main && !detailedLayers[main]) {
dispatch(fetchLayerAction(main));
}

if (compare && !detailedLayers[compare]) {
dispatch(fetchLayerAction(compare));
}
}, [dispatch, selectedLayers, detailedLayers]);

return null;
};

export default LayerLoader;
11 changes: 3 additions & 8 deletions src/scripts/components/layer-selector/layer-selector.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import React, {FunctionComponent, useEffect, useState} from 'react';
import React, {FunctionComponent, useState} from 'react';
import {useSelector, useDispatch} from 'react-redux';
import {useIntl} from 'react-intl';

import {layersSelector} from '../../reducers/layers';
import {selectedLayersSelector} from '../../reducers/selected-layers';
import {layersSelector} from '../../reducers/layers/list';
import {selectedLayersSelector} from '../../reducers/layers/selected';
import {LayersIcon} from '../icons/LayersIcon';
import {CompareIcon} from '../icons/CompareIcon';
import fetchLayers from '../../actions/fetch-layers';
import setSelectedLayerIdAction from '../../actions/set-selected-layer';
import LayerList from '../layer-list/layer-list';
import Tabs from '../tabs/tabs';
Expand Down Expand Up @@ -56,10 +55,6 @@ const LayerSelector: FunctionComponent = () => {
dispatch(setSelectedLayerIdAction(newId, isMainTabSelected));
};

useEffect(() => {
dispatch(fetchLayers());
}, []);

return (
<div className={styles.layerContainer}>
<Tabs
Expand Down
4 changes: 3 additions & 1 deletion src/scripts/config/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ const globeState: GlobeState = {

export default {
api: {
layers: 'https://storage.googleapis.com/esa-cfs-storage/layers',
layers: 'https://storage.googleapis.com/esa-cfs-storage/layers-{lang}.json',
layer:
'https://storage.googleapis.com/esa-cfs-storage/layers/{id}/metadata.json',
stories: 'https://storage.googleapis.com/esa-cfs-storage/stories'
},
globe: globeState
Expand Down
34 changes: 34 additions & 0 deletions src/scripts/libs/replace-url-placeholders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
type ValueMap = {
[key: string]: string;
};

const MATCH_PLACEHOLDERS = /{\w+}/g;

/**
* Replaces all url occurences of '{key}' with the provided values
*/
export function replaceUrlPlaceholders(url: string, values: ValueMap): string {
const matches = url.match(MATCH_PLACEHOLDERS);

if (!matches) {
return url;
}

matches.forEach(match => {
// remove {} to get key
const key = match.replace(/{|}/g, '');
// get value for this key
const value = values[key];

// do nothing if no value for this key is provided
if (!value) {
return;
}

// replace all placeholders for this key in the url with the value
const regex = new RegExp(`{${key}}`, 'g');
url = url.replace(regex, value);
});

return url;
}
4 changes: 1 addition & 3 deletions src/scripts/reducers/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import {combineReducers} from 'redux';

import languageReducer from './language';
import layersReducer from './layers';
import layersReducer from './layers/index';
import storiesReducer from './stories';
import selectedLayersReducer from './selected-layers';
import globeReducer from './globe';

const rootReducer = combineReducers({
language: languageReducer,
layers: layersReducer,
stories: storiesReducer,
selectedLayers: selectedLayersReducer,
globe: globeReducer
});

Expand Down
28 changes: 0 additions & 28 deletions src/scripts/reducers/layers.ts

This file was deleted.

30 changes: 30 additions & 0 deletions src/scripts/reducers/layers/details.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
FETCH_LAYER_SUCCESS,
FetchLayerSuccessAction
} from '../../actions/fetch-layer';

import {State} from '../index';
import {Layer} from '../../types/layer';

type DetailsById = {[id: string]: Layer};

function detailsReducer(
state: DetailsById = {},
action: FetchLayerSuccessAction
): DetailsById {
switch (action.type) {
case FETCH_LAYER_SUCCESS:
return {
...state,
[action.id]: action.layer
};
default:
return state;
}
}

export function detailedLayersSelector(state: State): DetailsById {
return state.layers.details;
}

export default detailsReducer;
19 changes: 19 additions & 0 deletions src/scripts/reducers/layers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {combineReducers} from 'redux';

import listReducer from './list';
import detailsReducer from './details';
import selectedReducer from './selected';

import {State} from '../index';

const layersReducer = combineReducers({
list: listReducer,
details: detailsReducer,
selected: selectedReducer
});

export default layersReducer;

export type LayersState = ReturnType<typeof layersReducer>;

export const layersStateSelector = (state: State): LayersState => state.layers;
27 changes: 27 additions & 0 deletions src/scripts/reducers/layers/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {
FETCH_LAYERS_SUCCESS,
FetchLayersActions
} from '../../actions/fetch-layers';

import {State} from '../index';
import {LayerList} from '../../types/layer-list';

const initialState: LayerList = [];

function layersReducer(
layersState: LayerList = initialState,
action: FetchLayersActions
): LayerList {
switch (action.type) {
case FETCH_LAYERS_SUCCESS:
return action.layers;
default:
return layersState;
}
}

export function layersSelector(state: State): LayerList {
return state.layers.list;
}

export default layersReducer;
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {
SET_SELECTED_LAYER_ID,
SetSelectedLayerIdAction
} from '../actions/set-selected-layer';
} from '../../actions/set-selected-layer';

import {State} from './index';
import {State} from '../index';

export interface SelectedLayersState {
main: string | null;
Expand All @@ -30,6 +30,6 @@ function selectedLayersReducer(
}
}
export function selectedLayersSelector(state: State): SelectedLayersState {
return state.selectedLayers;
return state.layers.selected;
}
export default selectedLayersReducer;
8 changes: 8 additions & 0 deletions src/scripts/types/layer-list.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
interface LayerListItem {
id: string;
name: string;
description: string;
subLayers: LayerListItem[];
}

export type LayerList = LayerListItem[];
Loading

0 comments on commit 7b62d20

Please sign in to comment.