Skip to content

Commit

Permalink
feat(story): fetch story (#166)
Browse files Browse the repository at this point in the history
* feat(story): fetch story

* feat(story): display story data in sidepanel

* feat(story): add slides to sidepanel

* refactor(story): remove unused import

* style(app): remove trailing whitespace

* fix(story): fix PR issues
  • Loading branch information
KatvonRivia authored and pwambach committed Oct 22, 2019
1 parent 6093e98 commit 6cc8f3a
Show file tree
Hide file tree
Showing 33 changed files with 216 additions and 95 deletions.
6 changes: 3 additions & 3 deletions src/scripts/actions/fetch-stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import fetchStoriesApi from '../api/fetch-stories';
import {languageSelector} from '../reducers/language';

import {State} from '../reducers/index';
import {Story} from '../types/story';
import {StoryList} from '../types/story-list';

export const FETCH_STORIES_SUCCESS = 'FETCH_STORIES_SUCCESS';
export const FETCH_STORIES_ERROR = 'FETCH_STORIES_ERROR';

interface FetchStoriesSuccessAction {
type: typeof FETCH_STORIES_SUCCESS;
stories: Story[];
stories: StoryList;
}

interface FetchStoriesErrorAction {
Expand All @@ -23,7 +23,7 @@ export type FetchStoriesActions =
| FetchStoriesSuccessAction
| FetchStoriesErrorAction;

function fetchStoriesSuccessAction(stories: Story[]) {
function fetchStoriesSuccessAction(stories: StoryList) {
return {
type: FETCH_STORIES_SUCCESS,
stories
Expand Down
49 changes: 49 additions & 0 deletions src/scripts/actions/fetch-story.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {Dispatch} from 'redux';

import fetchStoryApi from '../api/fetch-story';
import {languageSelector} from '../reducers/language';
import {State} from '../reducers/index';

import {Story} from '../types/story';

export const FETCH_STORY_SUCCESS = 'FETCH_STORY_SUCCESS';
export const FETCH_STORY_ERROR = 'FETCH_STORY_ERROR';

interface FetchStorySuccessAction {
type: typeof FETCH_STORY_SUCCESS;
story: Story;
}

interface FetchStoryErrorAction {
type: typeof FETCH_STORY_ERROR;
message: string;
}

export type FetchStoryActions = FetchStorySuccessAction | FetchStoryErrorAction;

function fetchStorySuccessAction(story: Story) {
return {
type: FETCH_STORY_SUCCESS,
story
};
}

function fetchStoryErrorAction(message: string) {
return {
type: FETCH_STORY_ERROR,
message
};
}

const fetchStory = (id: string) => (
dispatch: Dispatch,
getState: () => State
) => {
const language = languageSelector(getState());

return fetchStoryApi(id, language)
.then(story => dispatch(fetchStorySuccessAction(story)))
.catch(error => dispatch(fetchStoryErrorAction(error.message)));
};

export default fetchStory;
6 changes: 4 additions & 2 deletions src/scripts/api/fetch-stories.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import config from '../config/main';

import {replaceUrlPlaceholders} from '../libs/replace-url-placeholders';

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

export default function fetchStories(language: Language) {
const url = `${config.api.stories}-${language.toLowerCase()}.json`;
export default function fetchStories(lang: Language) {
const url = replaceUrlPlaceholders(config.api.stories, {lang});
return fetch(url).then(res => res.json());
}
10 changes: 10 additions & 0 deletions src/scripts/api/fetch-story.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import config from '../config/main';

import {replaceUrlPlaceholders} from '../libs/replace-url-placeholders';

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

export default function fetchStory(id: string, lang: Language) {
const url = replaceUrlPlaceholders(config.api.story, {id, lang});
return fetch(url).then(res => res.json());
}
10 changes: 8 additions & 2 deletions src/scripts/components/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {Provider as StoreProvider, useSelector} from 'react-redux';
import thunk from 'redux-thunk';
import {createLogger} from 'redux-logger';
import {IntlProvider} from 'react-intl';
import {HashRouter as Router, Switch, Route} from 'react-router-dom';
import {HashRouter as Router, Switch, Route, Redirect} from 'react-router-dom';

import rootReducer from '../../reducers/index';
import {languageSelector} from '../../reducers/language';
Expand Down Expand Up @@ -68,9 +68,15 @@ const TranslatedApp: FunctionComponent = () => {
<StoriesSelector />
</Route>

<Route path="/stories/:storyId">
<Route path="/stories/:storyId/:page">
<Story />
</Route>

<Route
path="/stories/:storyId"
render={props => (
<Redirect to={`${props.match.url}/0`} />
)}></Route>
</Switch>
</div>
</IntlProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React, {FunctionComponent} from 'react';

import {Story} from '../../types/story';
import {Link} from 'react-router-dom';

import styles from './story-item.styl';
import {StoryListItem as StoryListItemType} from '../../types/story-list';

import styles from './story-list-item.styl';

interface Props {
story: Story;
story: StoryListItemType;
}
const StoryItem: FunctionComponent<Props> = ({story}) => (
const StoryListItem: FunctionComponent<Props> = ({story}) => (
<Link to={`/stories/${story.id}`}>
<div className={styles.storyItem}>
<img src={story.image} className={styles.image} />
Expand All @@ -18,4 +18,4 @@ const StoryItem: FunctionComponent<Props> = ({story}) => (
</Link>
);

export default StoryItem;
export default StoryListItem;
4 changes: 2 additions & 2 deletions src/scripts/components/story-list/story-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {useSelector, useDispatch} from 'react-redux';

import {storiesSelector} from '../../reducers/stories';
import fetchStories from '../../actions/fetch-stories';
import StoryItem from '../story-item/story-item';
import StoryListItem from '../story-list-item/story-list-item';

import styles from './story-list.styl';

Expand All @@ -18,7 +18,7 @@ const StoryList: FunctionComponent = () => {
return (
<div className={styles.storyList}>
{stories.map(story => (
<StoryItem key={story.id} story={story} />
<StoryListItem key={story.id} story={story} />
))}
</div>
);
Expand Down
55 changes: 39 additions & 16 deletions src/scripts/components/story/story.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,45 @@
import React, {FunctionComponent} from 'react';
import React, {FunctionComponent, useEffect} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {useParams, Redirect} from 'react-router-dom';

import fetchStory from '../../actions/fetch-story';
import {storySelector} from '../../reducers/story';

import styles from './story.styl';

const Story: FunctionComponent = () => (
<div className={styles.story}>
<div className={styles.sidepanel}>
<div className={styles.previewImage}></div>
<div className={styles.content}>
<h1>Test</h1>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolor optio
nisi nobis quas ut. Exercitationem, sapiente. Praesentium quidem
mollitia explicabo voluptatem aperiam deleniti ut sunt atque eaque,
voluptate commodi in.
</p>
</div>
const Story: FunctionComponent = () => {
const story = useSelector(storySelector);
const dispatch = useDispatch();
const {storyId, page} = useParams();
const pageNumber = parseInt(page || '0', 10);
const slide = story && story.slides[pageNumber];
const activeStoryId = story && story.id;

useEffect(() => {
storyId && dispatch(fetchStory(storyId));
}, [storyId, dispatch]);

if (!slide) {
return <Redirect to={`/stories/${storyId}/0`} />;
}

if (activeStoryId !== storyId) {
return null;
}

return (
<div className={styles.story}>
{story && (
<div className={styles.sidepanel} key={slide.title}>
<img src={slide.image} className={styles.previewImage} />
<div className={styles.content}>
<h1>{slide.title}</h1>
<p>{slide.bodytext}</p>
</div>
</div>
)}
</div>
</div>
);
);
};

export default Story;
5 changes: 4 additions & 1 deletion src/scripts/config/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ export default {
'https://storage.googleapis.com/esa-cfs-storage/layers/layers-{lang}.json',
layer:
'https://storage.googleapis.com/esa-cfs-storage/layers/{id}/metadata.json',
stories: 'https://storage.googleapis.com/esa-cfs-storage/stories'
stories:
'https://storage.googleapis.com/esa-cfs-storage/stories/stories-{lang}.json',
story:
'https://storage.googleapis.com/esa-cfs-storage/stories/{id}/{id}-{lang}.json'
},
globe: globeState
};
2 changes: 2 additions & 0 deletions src/scripts/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import {combineReducers} from 'redux';
import languageReducer from './language';
import layersReducer from './layers/index';
import storiesReducer from './stories';
import storyReducer from './story';
import globeReducer from './globe';

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

Expand Down
9 changes: 4 additions & 5 deletions src/scripts/reducers/stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@ import {
FetchStoriesActions
} from '../actions/fetch-stories';

import {Story} from '../types/story';
import {StoryList} from '../types/story-list';
import {State} from './index';

export type StoriesState = Story[];
const initialState: StoriesState = [];
const initialState: StoryList = [];

function storiesReducer(
storiesState: StoriesState = initialState,
storiesState: StoryList = initialState,
action: FetchStoriesActions
): StoriesState {
): StoryList {
switch (action.type) {
case FETCH_STORIES_SUCCESS:
return action.stories;
Expand Down
22 changes: 22 additions & 0 deletions src/scripts/reducers/story.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {FETCH_STORY_SUCCESS, FetchStoryActions} from '../actions/fetch-story';

import {Story} from '../types/story';
import {State} from './index';

function storyReducer(
storyState: Story | null = null,
action: FetchStoryActions
): Story | null {
switch (action.type) {
case FETCH_STORY_SUCCESS:
return action.story;
default:
return storyState;
}
}

export function storySelector(state: State): Story | null {
return state.story;
}

export default storyReducer;
9 changes: 9 additions & 0 deletions src/scripts/types/story-list.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface StoryListItem {
id: string;
title: string;
description: string;
link: string;
image: string;
}

export type StoryList = StoryListItem[];
8 changes: 6 additions & 2 deletions src/scripts/types/story.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
export interface Story {
id: string;
slides: Slide[];
}

interface Slide {
title: string;
description: string;
link: string;
subtitle: string;
bodytext: string;
image: string;
}
24 changes: 8 additions & 16 deletions storage/stories/stories-de.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,48 @@
"id": "story1",
"title": "Geschichte 1",
"description": "This is story 1",
"link": "https://...",
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story1/img/story1.jpeg?supportedpurview=project"
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story1/assets/story1.jpeg"
},
{
"id": "story2",
"title": "Geschichte 2",
"description": "This is story 2",
"link": "https://...",
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story2/img/story2.jpeg?supportedpurview=project"
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story2/assets/story2.jpeg"
},
{
"id": "story3",
"title": "Geschichte 3",
"description": "Das ist Geschichte 3",
"link": "https://...",
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story3/img/story3.jpeg?supportedpurview=project"
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story3/assets/story3.jpeg"
},
{
"id": "story4",
"title": "Geschichte 4",
"description": "Das ist Geschichte 4",
"link": "https://...",
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story4/img/story4.jpeg?supportedpurview=project"
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story4/assets/story4.jpeg"
},
{
"id": "story5",
"title": "Geschichte 5",
"description": "Das ist Geschichte 5",
"link": "https://...",
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story5/img/story5.jpeg?supportedpurview=project"
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story5/assets/story5.jpeg"
},
{
"id": "story6",
"title": "Geschichte 6",
"description": "Das ist Geschichte 6",
"link": "https://...",
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story6/img/story6.jpeg?supportedpurview=project"
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story6/assets/story6.jpeg"
},
{
"id": "story7",
"title": "Geschichte 7",
"description": "Das ist Geschichte 7",
"link": "https://...",
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story7/img/story7.jpeg?supportedpurview=project"
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story7/assets/story7.jpeg"
},
{
"id": "story8",
"title": "Geschichte 8",
"description": "Das ist Geschichte 8",
"link": "https://...",
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story8/img/story8.jpeg?supportedpurview=project"
"image": "https://storage.cloud.google.com/esa-cfs-storage/stories/story8/assets/story8.jpeg"
}
]
Loading

0 comments on commit 6cc8f3a

Please sign in to comment.