├── .babelrc ├── .gitignore ├── LICENSE ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── images │ ├── cat.jpg │ ├── dog.png │ └── dogs.gif ├── index.html ├── output.js └── styles.css ├── readme.md ├── src ├── app │ ├── App.js │ └── components │ │ ├── AstronomyCard.js │ │ └── AstronomyContainer.js ├── assets │ └── stylesheets │ │ └── styles.scss └── index.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", "react" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Indrek Lasn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-2.0-from-scratch", 3 | "version": "2.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack --progress", 8 | "start": "webpack --progress && webpack-dev-server -d --hot --config webpack.config.js --watch --progress", 9 | "production": "NODE_ENV=production webpack --progress" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "devDependencies": { 15 | "autoprefixer": "^7.1.1", 16 | "babel-core": "^6.24.1", 17 | "babel-loader": "^7.0.0", 18 | "babel-preset-es2015": "^6.24.1", 19 | "babel-preset-react": "^6.24.1", 20 | "css-hot-loader": "^1.0.4", 21 | "css-loader": "^0.28.0", 22 | "extract-text-webpack-plugin": "^2.1.0", 23 | "file-loader": "^0.11.1", 24 | "image-webpack-loader": "^3.3.0", 25 | "node-sass": "^4.5.2", 26 | "optimize-css-assets-webpack-plugin": "^1.3.1", 27 | "postcss-loader": "^2.0.5", 28 | "react": "^15.5.4", 29 | "react-dom": "^15.5.4", 30 | "sass-loader": "^6.0.3", 31 | "style-loader": "^0.16.1", 32 | "uglifyjs-webpack-plugin": "^0.4.3", 33 | "webpack": "^2.3.3", 34 | "webpack-dev-server": "^2.4.2" 35 | }, 36 | "dependencies": { 37 | "axios": "^0.16.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer') 4 | ] 5 | } -------------------------------------------------------------------------------- /public/images/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indreklasn/nasa-react-redux/552711d915086c2b1486bf7d4591c69e3b8f58a6/public/images/cat.jpg -------------------------------------------------------------------------------- /public/images/dog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indreklasn/nasa-react-redux/552711d915086c2b1486bf7d4591c69e3b8f58a6/public/images/dog.png -------------------------------------------------------------------------------- /public/images/dogs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indreklasn/nasa-react-redux/552711d915086c2b1486bf7d4591c69e3b8f58a6/public/images/dogs.gif -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Webpack 2.0 from scratch! 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /public/styles.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Merriweather);body { 2 | height: 100vh; 3 | background: #eaeaea; 4 | color: rgba(0, 0, 0, 0.87); 5 | font-size: 5rem; 6 | font-weight: 900; 7 | font-family: "Merriweather", sans-serif; 8 | padding: 0; 9 | margin: 0; } 10 | 11 | .astronomy-card { 12 | max-width: 75vw; 13 | margin: 50px auto; 14 | display: -webkit-box; 15 | display: -ms-flexbox; 16 | display: flex; 17 | background-color: #FFF; 18 | -webkit-box-orient: vertical; 19 | -webkit-box-direction: normal; 20 | -ms-flex-direction: column; 21 | flex-direction: column; 22 | -webkit-box-pack: center; 23 | -ms-flex-pack: center; 24 | justify-content: center; 25 | -webkit-box-align: center; 26 | -ms-flex-align: center; 27 | align-items: center; 28 | -webkit-box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); 29 | box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); } 30 | .astronomy-card .astronomy-title { 31 | margin: 25px 0; 32 | padding: 15px 45px; 33 | font-size: 18px; 34 | color: #6200ea; } 35 | .astronomy-card .astronomy-image-wrapper { 36 | padding: 10px; } 37 | .astronomy-card .astronomy-image-wrapper img { 38 | cursor: pointer; 39 | max-height: 500px; 40 | -webkit-filter: drop-shadow(6px 6px 12px rgba(0, 0, 0, 0.45)); 41 | filter: drop-shadow(6px 6px 12px rgba(0, 0, 0, 0.45)); 42 | -webkit-transition: all 325ms ease-in-out; 43 | transition: all 325ms ease-in-out; } 44 | .astronomy-card .astronomy-image-wrapper img:hover { 45 | -webkit-transition: all 325ms ease-in-out; 46 | transition: all 325ms ease-in-out; 47 | -webkit-transform: scale(1.07); 48 | transform: scale(1.07); } 49 | .astronomy-card p { 50 | font-size: 12px; 51 | padding: 10px 50px; 52 | letter-spacing: 0.66px; 53 | line-height: 2; } 54 | .astronomy-card span { 55 | font-size: 10px; 56 | color: #311b92; 57 | margin: 20px 0; } 58 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Learn How To Build: Astronomy Picture Of The Day App with NASA API and React + Redux 2 | 3 | ### Demo (image/video is different everyday!) 4 | 5 | ![demo](https://i.imgur.com/jWxdlKT.jpg) 6 | 7 | ## https://medium.com/@wesharehoodies/learn-how-to-build-astronomy-picture-of-the-day-app-with-nasa-api-and-react-redux-part-ii-83f15970d0e3 8 | 9 | ## How to start 10 | 11 | ```bash 12 | npm install 13 | ``` 14 | 15 | ``` 16 | npm run start 17 | ``` 18 | 19 | ``` 20 | npm run production 21 | ``` 22 | 23 | ``` 24 | npm run build 25 | ``` 26 | -------------------------------------------------------------------------------- /src/app/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import AstronomyContainer from './components/AstronomyContainer'; 3 | 4 | const App = () => { 5 | return ( 6 | 7 | ) 8 | } 9 | 10 | export default App; 11 | -------------------------------------------------------------------------------- /src/app/components/AstronomyCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const AstronomyCard = (props) => { 4 | 5 | const { title, 6 | url, 7 | hdurl, 8 | explanation, 9 | date, 10 | copyright, 11 | media_type 12 | } = props.data; 13 | 14 | function renderContent() { 15 | switch(media_type) { 16 | 17 | case('video'): 18 | return ( 19 | 26 | ) 27 | 28 | case('image'): 29 | return ( 30 | 31 | {title} 32 | 33 | ) 34 | 35 | default: 36 | return null 37 | } 38 | } 39 | 40 | return ( 41 |
42 | 43 |
{title}
44 | 45 | {renderContent()} 46 | 47 |

{explanation}

48 | 49 | {date}, {copyright} 50 | 51 |
52 | ) 53 | } 54 | 55 | export default AstronomyCard; -------------------------------------------------------------------------------- /src/app/components/AstronomyContainer.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import AstronomyCard from './AstronomyCard'; 3 | import axios from 'axios'; 4 | 5 | class AstronomyContainer extends Component { 6 | 7 | constructor() { 8 | super(); 9 | 10 | this.state = { 11 | astronomy: [] 12 | } 13 | 14 | } 15 | 16 | componentDidMount() { 17 | const API_KEY = 'nxKl8yTvpvsXEqRz06mTPnn29uyckFmFCYrnqEIz'; 18 | const END_POINT = 'https://api.nasa.gov/planetary/apod?api_key=' 19 | 20 | axios.get(END_POINT+API_KEY) 21 | .then(response => { 22 | 23 | this.setState({ 24 | astronomy: response.data 25 | }) 26 | 27 | }) 28 | .catch(error => { 29 | console.log(error, 'failed to fetch data') 30 | }); 31 | } 32 | 33 | render() { 34 | const { astronomy } = this.state; 35 | return ( 36 | 37 | ) 38 | } 39 | } 40 | 41 | export default AstronomyContainer; -------------------------------------------------------------------------------- /src/assets/stylesheets/styles.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Merriweather'); 2 | 3 | 4 | $brand-purple: rgb(135,140,223); 5 | $brand-green: rgb(153,251,198); 6 | $font-size: 5rem; 7 | $merriweather: "Merriweather", sans-serif; 8 | 9 | body { 10 | height: 100vh; 11 | background: #eaeaea; 12 | color: rgba(0, 0, 0, 0.87); 13 | font-size: $font-size; 14 | font-weight: 900; 15 | font-family: $merriweather; 16 | padding: 0; 17 | margin: 0; 18 | } 19 | 20 | 21 | .astronomy-card { 22 | max-width: 75vw; 23 | margin: 50px auto; 24 | display: flex; 25 | background-color: #FFF; 26 | flex-direction: column; 27 | justify-content: center; 28 | align-items: center; 29 | box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); 30 | 31 | .astronomy-title { 32 | margin: 25px 0; 33 | padding: 15px 45px; 34 | font-size: 18px; 35 | color: #6200ea; 36 | } 37 | 38 | .astronomy-image-wrapper { 39 | padding: 10px; 40 | 41 | img { 42 | cursor: pointer; 43 | max-height: 500px; 44 | filter: drop-shadow(6px 6px 12px rgba(0,0,0,0.45)); 45 | transition: all 325ms ease-in-out; 46 | &:hover { 47 | transition: all 325ms ease-in-out; 48 | transform: scale(1.07); 49 | } 50 | } 51 | } 52 | 53 | p { 54 | font-size: 12px; 55 | padding: 10px 50px; 56 | letter-spacing: 0.66px; 57 | line-height: 2; 58 | } 59 | 60 | span { 61 | font-size: 10px; 62 | color: #311b92; 63 | margin: 20px 0; 64 | } 65 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; // import the main react dependency 2 | import ReactDOM from 'react-dom'; // import reactDOM 3 | import App from './app/App'; // import the main app component 4 | 5 | import './assets/stylesheets/styles.scss'; 6 | 7 | ReactDOM.render( 8 | , // render our App component 9 | document.getElementById('root') // and mount it to our #root element 10 | ); 11 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); // webpack itself 2 | const path = require('path'); // nodejs dependency when dealing with paths 3 | const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin'); // require webpack plugin 4 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); // require webpack plugin 5 | const OptimizeCSSAssets = require('optimize-css-assets-webpack-plugin'); // require webpack plugin 6 | 7 | let config = { // config object 8 | entry: './src/index.js', // entry file 9 | output: { // output 10 | path: path.resolve(__dirname, 'public'), // ouput path 11 | filename: 'output.js' // output filename 12 | }, 13 | resolve: { // These options change how modules are resolved 14 | extensions: ['.js', '.jsx', '.json', '.scss', '.css', '.jpeg', '.jpg', '.gif', '.png'], // Automatically resolve certain extensions 15 | alias: { // Create aliases 16 | images: path.resolve(__dirname, 'src/assets/images') // src/assets/images alias 17 | } 18 | }, 19 | module: { 20 | rules: [ // loader rules 21 | { 22 | test: /\.js$/, // files ending with .js 23 | exclude: /node_modules/, // exclude the node_modules directory 24 | loader: 'babel-loader' // use this (babel-core) loader 25 | }, 26 | { 27 | test: /\.scss$/, // files ending with .scss 28 | use: ['css-hot-loader'].concat(ExtractTextWebpackPlugin.extract({ // HMR for styles 29 | fallback: 'style-loader', 30 | use: ['css-loader', 'sass-loader', 'postcss-loader'], 31 | })), 32 | }, 33 | { 34 | test: /\.jsx$/, // all files ending with .jsx 35 | loader: 'babel-loader', // use the babel-loader for all .jsx files 36 | exclude: /node_modules/ // exclude searching for files in the node_modules directory 37 | }, 38 | { 39 | test: /\.(jpe?g|png|gif|svg)$/i, 40 | loaders: ['file-loader?context=src/assets/images/&name=images/[path][name].[ext]', { // images loader 41 | loader: 'image-webpack-loader', 42 | query: { 43 | mozjpeg: { 44 | progressive: true, 45 | }, 46 | gifsicle: { 47 | interlaced: false, 48 | }, 49 | optipng: { 50 | optimizationLevel: 4, 51 | }, 52 | pngquant: { 53 | quality: '75-90', 54 | speed: 3, 55 | }, 56 | }, 57 | }], 58 | exclude: /node_modules/, 59 | include: __dirname, 60 | }, 61 | ] // end rules 62 | }, 63 | plugins: [ // webpack plugins 64 | new ExtractTextWebpackPlugin('styles.css'), // call the ExtractTextWebpackPlugin constructor and name our css file 65 | ], 66 | devServer: { 67 | contentBase: path.resolve(__dirname, 'public'), // A directory or URL to serve HTML content from. 68 | historyApiFallback: true, // fallback to /index.html for Single Page Applications. 69 | inline: true, // inline mode (set to false to disable including client scripts (like livereload) 70 | open: true, // open default browser while launching 71 | compress: true, // Enable gzip compression for everything served: 72 | hot: true // Enable webpack's Hot Module Replacement feature 73 | }, 74 | devtool: 'eval-source-map', // enable devtool for better debugging experience 75 | } 76 | 77 | module.exports = config; 78 | 79 | if (process.env.NODE_ENV === 'production') { // if we're in production mode, here's what happens next 80 | module.exports.plugins.push( 81 | new webpack.optimize.UglifyJsPlugin(), // call the uglify plugin 82 | new OptimizeCSSAssets() // call the css optimizer (minfication) 83 | ); 84 | } 85 | --------------------------------------------------------------------------------