-
Notifications
You must be signed in to change notification settings - Fork 9
Project - Jonas Høier Christiansen #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| FROM node:10 | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| EXPOSE 3000 | ||
|
|
||
| COPY package.json package.json | ||
| RUN yarn install | ||
|
|
||
| COPY . /app | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hvis jeg allerede har en node_modules liggende, vil den overskrive de node_modules som lige er blevet installeret på linje 8. |
||
|
|
||
| RUN yarn build | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Jeg kan ikke bygge din docker image: |
||
|
|
||
| CMD [ "yarn", "start" ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,26 @@ | ||
| import * as types from './types'; | ||
| import { relativeTimeRounding } from 'moment'; | ||
|
|
||
|
|
||
| const sleep = (seconds, withValue) => new Promise(resolve => setTimeout(resolve, seconds * 1000, withValue)); | ||
|
|
||
| export const onChooseEndpoint = (endpoint) => (dispatch, getState) => { | ||
| dispatch({ type: types.PICK_ENDPOINT, payload: endpoint }); | ||
| dispatch({ type: types.START_LOAD }); | ||
|
|
||
| const dataCached = getState().data[endpoint] | ||
|
|
||
| if (!dataCached || dataCached.length === 0) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Vil det sige at du ikke stoler på cache, hvis den indeholder 0 items? |
||
| dispatch({ type: types.START_LOAD }); | ||
|
|
||
| return loadData('https://swapi.co/api/' + endpoint + '/') | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Overvej template string |
||
| .catch((err) => { | ||
| console.error(err); | ||
| alert('It went wrong.') | ||
| }) | ||
| .then(() => dispatch({ type: types.DONE_LOAD })) | ||
| } else { | ||
| return; | ||
| } | ||
|
|
||
| function loadData(url, loadedData = []) { | ||
| dispatch({ type: types.START_LOAD }); | ||
|
|
@@ -13,7 +29,9 @@ export const onChooseEndpoint = (endpoint) => (dispatch, getState) => { | |
| .then((response) => response.json()) | ||
| .then((json) => { | ||
| const result = json.results.map((item) => ({ ...item, name: item.title || item.name, kind: endpoint })); | ||
| loadedData = loadedData.concat(result); | ||
|
|
||
| loadedData = loadedData.concat(result) | ||
| loadedData = loadedData.sort((a, b) => (a.name.toString()).localeCompare(b.name.toString(), 'en')); | ||
|
|
||
| dispatch({ | ||
| type: types.SET_DATA, | ||
|
|
@@ -34,11 +52,21 @@ export const onChooseEndpoint = (endpoint) => (dispatch, getState) => { | |
| .catch(() => console.error('Oops')) | ||
|
|
||
| } | ||
| }; | ||
|
|
||
| export const onExpandToggle = (item) => (dispatch) => { | ||
| dispatch({ type: types.TOGGLE_EXPAND_ITEM, payload: item }); | ||
| }; | ||
|
|
||
| export const onMergeLists = (endpoint) => (dispatch, getState) => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Jeg har lidt svært med at gennemskue meningen med denne action? Og da jeg ikke kan bygge, og dermed heller ikke kan køre, din docker image kan jeg heller ikke se/opleve hvad den gør i browseren. |
||
| dispatch({ type: types.START_LOAD }); | ||
| dispatch({ type: types.PICK_ENDPOINT, payload: endpoint }); | ||
|
|
||
| const dataCached = getState().data[endpoint] | ||
|
|
||
| if (!dataCached) { | ||
| dispatch({ type: types.MERGE_LISTS, payload: endpoint }); | ||
| } | ||
|
|
||
| return loadData('https://swapi.co/api/' + endpoint + '/') | ||
| .catch((err) => { | ||
| console.error(err); | ||
| alert('It went wrong.') | ||
| }) | ||
| .then(() => dispatch({ type: types.DONE_LOAD })) | ||
| dispatch({ type: types.DONE_LOAD }); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,37 +1,58 @@ | ||
| import React from 'react'; | ||
| import PropTypes from 'prop-types'; | ||
| import classNames from 'classnames'; | ||
|
|
||
| export default class ItemDisplay extends React.PureComponent { | ||
|
|
||
| static propTypes = { | ||
| children: PropTypes.shape({ | ||
| url: PropTypes.string.isRequired, | ||
| name: PropTypes.string.isRequired, | ||
| kind: PropTypes.string.isRequired, | ||
| }) | ||
| }; | ||
|
|
||
| state = { | ||
| isExpanded: false, | ||
| url: PropTypes.string.isRequired, | ||
| expanded: PropTypes.bool, | ||
| episode_id: PropTypes.number, | ||
| gender: PropTypes.string, | ||
| }), | ||
| }; | ||
|
|
||
| handleExpandToggle = () => { | ||
| this.setState((state) => ({ isExpanded: !state.isExpanded })); | ||
| expandToggle = () => { | ||
| this.props.setExpandToggle(this.props.children.url) | ||
| }; | ||
|
|
||
| render() { | ||
| const { children: { name }, onExpandToggle} = this.props; | ||
| const { isExpanded } = this.state; | ||
| const { children: { name, kind, expanded, gender, episode_id, charactersNames }, endpoint} = this.props; | ||
|
|
||
| const iconKindClass = classNames('fa', { | ||
| 'fa-female': kind === 'people' && gender === 'female', | ||
| 'fa-male': kind === 'people' && gender === 'male', | ||
| 'fa-android': kind === 'people' && (gender === 'none' || gender === 'n/a'), | ||
| 'fa-transgender-alt': kind === 'people' && gender === 'hermaphrodite', | ||
| 'fa-flag': episode_id, | ||
| }); | ||
|
|
||
| return (<span> | ||
| <button className="btn btn-xs btn-default" onClick={this.handleExpandToggle} aria-label={isExpanded ? 'Collapse' : 'Expand'}> | ||
| {isExpanded | ||
| ? <i className="fa fa-chevron-circle-up" /> | ||
| : <i className="fa fa-chevron-circle-down" />} | ||
| </button> | ||
| {' '} | ||
| {name} | ||
| { isExpanded ? <pre>{JSON.stringify(this.props.children, null, 4)}</pre> : null} | ||
| </span>); | ||
| return ( | ||
| <span> | ||
|
|
||
| <button className="btn btn-xs btn-default" onClick={this.expandToggle} aria-label={expanded > 0 ? 'Collapse' : 'Expand'}> | ||
| {expanded > 0 | ||
| ? <i className="fa fa-chevron-circle-up" /> | ||
| : <i className="fa fa-chevron-circle-down" /> | ||
| } | ||
| </button> | ||
| {' '} | ||
| {name} - <i className={iconKindClass}/> | ||
| {' '} | ||
| { episode_id && | ||
| <span> Episode {episode_id}</span> | ||
| } | ||
| {' '} | ||
| { endpoint === 'charactersNames' && | ||
| <span> - <span className='fa fa-users'> {charactersNames.length}</span></span> | ||
| } | ||
| { endpoint === 'charactersNames' | ||
| ? expanded ? <pre>{JSON.stringify(charactersNames, null, 4)}</pre> : null | ||
| : expanded ? <pre>{JSON.stringify(this.props.children, null, 4)}</pre> : null | ||
| } | ||
| </span>); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,32 +7,58 @@ export default class MillenniumFalcon extends React.PureComponent { | |
|
|
||
| static propTypes = { | ||
| onChooseEndpoint: PropTypes.func.isRequired, | ||
| onExpandToggle: PropTypes.func.isRequired, | ||
| onMergeLists: PropTypes.func.isRequired, | ||
| list: PropTypes.arrayOf(PropTypes.shape({ | ||
| url: PropTypes.string.isRequired, | ||
| })).isRequired, | ||
| side: PropTypes.string, | ||
| }; | ||
|
|
||
| render() { | ||
| const { loading, list, onChooseEndpoint } = this.props; | ||
| const { loading, list, onChooseEndpoint, onExpandToggle, onMergeLists, endpoint } = this.props; | ||
|
|
||
| const listName = endpoint === 'people' ? endpoint : 'films' | ||
|
|
||
| const iconClass = classNames('fa', { | ||
| 'fa-refresh': loading, | ||
| 'fa-spin': loading, | ||
| 'fa-star': !loading, | ||
| }); | ||
|
|
||
| return (<div> | ||
| <p><i className={iconClass} /> What do you want to see?</p> | ||
| <div className="btn-group"> | ||
| <button disabled={loading} onClick={() => onChooseEndpoint('people')} className="btn btn-danger">People</button> | ||
| <button disabled={loading} onClick={() => onChooseEndpoint('films')} className="btn btn-danger">Films</button> | ||
| return ( | ||
| <div> | ||
| <p> | ||
| <i className={iconClass} /> | ||
| {' '} | ||
| {loading | ||
| ? <span>Loading {endpoint}, please wait...</span> | ||
| : <span>What do you want to see?</span> | ||
| } | ||
| <br /> | ||
| {!this.props.data.films || !this.props.data.people | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Jeg kan ikke se at componenten modtage en |
||
| ? <span>Hint: A secret button will appear when you have seen both the lists of characters and films</span> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lidt sjov hemmelighed, hvis du afslører den med det samme 🤣 |
||
| : <span>You can now see the new button</span> | ||
| } | ||
| </p> | ||
| <div className="btn-group"> | ||
| <button disabled={loading} onClick={() => onChooseEndpoint('people')} className={"btn " + (endpoint === 'people' ? 'btn-success' : 'btn-primary')}><i className="fa fa-male"/><i className="fa fa-female"/> Characters</button> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Læs evt her hvorfor arrow functions (eller bind) direkte i JSX sjældent er en god ide https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md |
||
| <button disabled={loading} onClick={() => onChooseEndpoint('films')} className={"btn " + (endpoint === 'films' ? 'btn-success' : 'btn-primary')}><i className="fa fa-film"/> Films</button> | ||
| {this.props.data.films && this.props.data.people && !loading && | ||
| <button onClick={() => onMergeLists('charactersNames')} className={"btn " + (endpoint === 'charactersNames' ? 'btn-success' : 'btn-primary')}><i className="fa fa-users"/> Cast</button> | ||
| } | ||
| </div> | ||
| <br /><br /> | ||
| <p>{!loading && endpoint && | ||
| <span>{listName.toUpperCase()}({this.props.data[endpoint].length})</span> | ||
| } | ||
| </p> | ||
| <table className="table"> | ||
| <tbody> | ||
| {list.map((item) => <tr key={item.url}><td><ItemDisplay endpoint={endpoint} setExpandToggle={onExpandToggle}>{item}</ItemDisplay></td></tr>)} | ||
| </tbody> | ||
| </table> | ||
| </div> | ||
| <br /><br /> | ||
| <table className="table"><tbody> | ||
| {list.map((item) => <tr key={item.url}><td><ItemDisplay>{item}</ItemDisplay></td></tr>)} | ||
| </tbody> | ||
| </table> | ||
| </div>); | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,6 +43,30 @@ const reducer = (state = {}, action) => { | |
| operations: state.operations - 1, | ||
| } | ||
| } | ||
|
|
||
| case types.TOGGLE_EXPAND_ITEM: { | ||
| return { | ||
| ...state, | ||
| data: { | ||
| ...state.data, | ||
| [state.endpoint]: state.data[state.endpoint].map(item => item.url === action.payload ? { ...item, expanded: !item.expanded } : item) | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
| case types.MERGE_LISTS: { | ||
| return { | ||
| ...state, | ||
| data: { | ||
| ...state.data, | ||
| [action.payload]: state.data.films.map(film => ({ ...film, charactersNames: film.characters.map(character => ({ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Kan gøres nemmere at læse med lidt linebreaks de rigtige steder. |
||
| name: state.data.people.filter(person => person.url === character)[0].name | ||
| })).sort((a, b) => (a.name.toString()).localeCompare(b.name.toString(), 'en')) })) | ||
| } | ||
| } | ||
|
|
||
| } | ||
| } | ||
|
|
||
| return state; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mangler der måske også en yarn.lock?