├── 10
├── .DS_Store
├── actions
│ └── actionCreators.js
├── components
│ ├── Main.js
│ ├── PhotoGrid.js
│ └── Single.js
├── data
│ ├── comments.js
│ ├── config.js
│ └── posts.js
├── reducers
│ ├── comments.js
│ ├── index.js
│ └── posts.js
├── reduxstagram.js
├── store.js
└── styles
│ ├── _animations.styl
│ ├── _normalize.styl
│ ├── _typography.styl
│ └── style.styl
├── 13
├── .DS_Store
├── actions
│ └── actionCreators.js
├── components
│ ├── App.js
│ ├── Comments.js
│ ├── Main.js
│ ├── Photo.js
│ ├── PhotoGrid.js
│ └── Single.js
├── data
│ ├── comments.js
│ ├── config.js
│ └── posts.js
├── reducers
│ ├── comments.js
│ ├── index.js
│ └── posts.js
├── reduxstagram.js
├── store.js
└── styles
│ ├── _animations.styl
│ ├── _normalize.styl
│ ├── _typography.styl
│ └── style.styl
├── 14
├── .DS_Store
├── actions
│ └── actionCreators.js
├── components
│ ├── App.js
│ ├── Comments.js
│ ├── Main.js
│ ├── Photo.js
│ ├── PhotoGrid.js
│ └── Single.js
├── data
│ ├── comments.js
│ ├── config.js
│ └── posts.js
├── reducers
│ ├── comments.js
│ ├── index.js
│ └── posts.js
├── reduxstagram.js
├── store.js
└── styles
│ ├── _animations.styl
│ ├── _normalize.styl
│ ├── _typography.styl
│ └── style.styl
├── 15
├── .DS_Store
├── actions
│ └── actionCreators.js
├── components
│ ├── App.js
│ ├── Comments.js
│ ├── Main.js
│ ├── Photo.js
│ ├── PhotoGrid.js
│ └── Single.js
├── data
│ ├── comments.js
│ ├── config.js
│ └── posts.js
├── reducers
│ ├── comments.js
│ ├── index.js
│ └── posts.js
├── reduxstagram.js
├── store.js
└── styles
│ ├── _animations.styl
│ ├── _normalize.styl
│ ├── _typography.styl
│ └── style.styl
├── 16
├── .DS_Store
├── actions
│ └── actionCreators.js
├── components
│ ├── App.js
│ ├── Comments.js
│ ├── Main.js
│ ├── Photo.js
│ ├── PhotoGrid.js
│ └── Single.js
├── data
│ ├── comments.js
│ ├── config.js
│ └── posts.js
├── reducers
│ ├── comments.js
│ ├── index.js
│ └── posts.js
├── reduxstagram.js
├── store.js
└── styles
│ ├── _animations.styl
│ ├── _normalize.styl
│ ├── _typography.styl
│ └── style.styl
├── 17
├── .DS_Store
├── actions
│ └── actionCreators.js
├── components
│ ├── App.js
│ ├── Comments.js
│ ├── Main.js
│ ├── Photo.js
│ ├── PhotoGrid.js
│ └── Single.js
├── data
│ ├── comments.js
│ ├── config.js
│ └── posts.js
├── reducers
│ ├── comments.js
│ ├── index.js
│ └── posts.js
├── reduxstagram.js
├── store.js
└── styles
│ ├── _animations.styl
│ ├── _normalize.styl
│ ├── _typography.styl
│ └── style.styl
├── 18
├── .DS_Store
├── actions
│ └── actionCreators.js
├── components
│ ├── App.js
│ ├── Comments.js
│ ├── Main.js
│ ├── Photo.js
│ ├── PhotoGrid.js
│ └── Single.js
├── data
│ ├── comments.js
│ ├── config.js
│ └── posts.js
├── reducers
│ ├── comments.js
│ ├── index.js
│ └── posts.js
├── reduxstagram.js
├── store.js
└── styles
│ ├── _animations.styl
│ ├── _normalize.styl
│ ├── _typography.styl
│ └── style.styl
├── 19
├── actions
│ └── actionCreators.js
├── components
│ ├── App.js
│ ├── Comments.js
│ ├── Main.js
│ ├── Photo.js
│ ├── PhotoGrid.js
│ └── Single.js
├── data
│ ├── comments.js
│ ├── config.js
│ └── posts.js
├── reducers
│ ├── comments.js
│ ├── index.js
│ └── posts.js
├── reduxstagram.js
├── store.js
└── styles
│ ├── _animations.styl
│ ├── _normalize.styl
│ ├── _typography.styl
│ └── style.styl
├── .DS_Store
├── .gitignore
├── 05
├── .DS_Store
├── components
│ ├── Main.js
│ ├── PhotoGrid.js
│ └── Single.js
├── reduxstagram.js
└── styles
│ ├── _animations.styl
│ ├── _normalize.styl
│ ├── _typography.styl
│ └── style.styl
├── 06
├── .DS_Store
├── components
│ ├── Main.js
│ ├── PhotoGrid.js
│ └── Single.js
├── data
│ ├── comments.js
│ ├── config.js
│ └── posts.js
├── reduxstagram.js
├── store.js
└── styles
│ ├── _animations.styl
│ ├── _normalize.styl
│ ├── _typography.styl
│ └── style.styl
├── 07
├── .DS_Store
├── actions
│ └── actionCreators.js
├── components
│ ├── Main.js
│ ├── PhotoGrid.js
│ └── Single.js
├── data
│ ├── comments.js
│ ├── config.js
│ └── posts.js
├── reduxstagram.js
├── store.js
└── styles
│ ├── _animations.styl
│ ├── _normalize.styl
│ ├── _typography.styl
│ └── style.styl
├── 08
├── .DS_Store
├── actions
│ └── actionCreators.js
├── components
│ ├── Main.js
│ ├── PhotoGrid.js
│ └── Single.js
├── data
│ ├── comments.js
│ ├── config.js
│ └── posts.js
├── reducers
│ ├── comments.js
│ ├── index.js
│ └── posts.js
├── reduxstagram.js
├── store.js
└── styles
│ ├── _animations.styl
│ ├── _normalize.styl
│ ├── _typography.styl
│ └── style.styl
├── learn-redux
├── .DS_Store
├── .babelrc
├── .eslintrc
├── client
│ ├── .DS_Store
│ ├── data
│ │ ├── comments.js
│ │ ├── config.js
│ │ └── posts.js
│ ├── reduxstagram.js
│ └── styles
│ │ ├── _animations.styl
│ │ ├── _normalize.styl
│ │ ├── _typography.styl
│ │ └── style.styl
├── devServer.js
├── index.html
├── package.json
├── readme.md
├── webpack.config.dev.js
└── webpack.config.prod.js
└── readme.md
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | node_modules
28 |
29 | # Optional npm cache directory
30 | .npm
31 |
32 | # Optional REPL history
33 | .node_repl_history
34 |
--------------------------------------------------------------------------------
/05/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/05/.DS_Store
--------------------------------------------------------------------------------
/05/components/Main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | const Main = React.createClass({
5 | render() {
6 | return (
7 |
8 |
9 | Reduxstagram
10 |
11 | {React.cloneElement(this.props.children, this.props)}
12 |
13 | )
14 | }
15 | });
16 |
17 | export default Main;
18 |
--------------------------------------------------------------------------------
/05/components/PhotoGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const PhotoGrid = React.createClass({
4 | render() {
5 | return (
6 |
7 | I'm the photo grid
8 |
9 | )
10 | }
11 | });
12 |
13 | export default PhotoGrid;
14 |
--------------------------------------------------------------------------------
/05/components/Single.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Single = React.createClass({
4 | render() {
5 | return (
6 |
7 | I'm the single
8 |
9 | )
10 | }
11 | });
12 |
13 | export default Single;
14 |
--------------------------------------------------------------------------------
/05/reduxstagram.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { render } from 'react-dom';
4 |
5 | // Import css
6 | import css from './styles/style.styl';
7 |
8 | // Import Components
9 | import Main from './components/Main';
10 | import Single from './components/Single';
11 | import PhotoGrid from './components/PhotoGrid';
12 |
13 | // import react router deps
14 | import { Router, Route, IndexRoute, browserHistory } from 'react-router';
15 |
16 | const router = (
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 |
25 | render(router, document.getElementById('root'));
26 |
--------------------------------------------------------------------------------
/05/styles/_animations.styl:
--------------------------------------------------------------------------------
1 | // offset variable gets tacked for centering in addition to the scaling
2 |
3 | offsets = translateX(-50%) translateY(-50%)
4 | .likes-heart
5 | opacity 0
6 | transition all 0.5s // time to fade out after its done
7 | transform offsets scale(5) // this is the "end state"
8 | display block
9 | &.like-enter
10 | transition all .2s
11 | transform offsets scale(1)
12 | opacity 1
13 | &.like-enter-active
14 | transform offsets scale(5)
15 | .like-leave-active
16 | display none
17 |
18 |
--------------------------------------------------------------------------------
/05/styles/_normalize.styl:
--------------------------------------------------------------------------------
1 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button],/* 1 */
2 | input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
3 |
4 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
5 |
6 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
7 |
8 |
9 | img
10 | max-width 100%
11 |
--------------------------------------------------------------------------------
/05/styles/_typography.styl:
--------------------------------------------------------------------------------
1 | /*
2 | Variables
3 | */
4 |
5 | blue = #125688
6 | offwhite = #fafafa
7 | lightgrey = #EDEEED
8 | lightgray = lightgrey // OH Canada!
9 |
10 | html
11 | font-size 10px
12 | font-family sans-serif
13 |
14 | p
15 | font-size 1.6rem
16 | line-height 1.5
17 |
18 | h1
19 | font-family billabong, 'billabongregular'
20 | text-align center
21 | font-weight 100
22 | font-size 13rem
23 | margin 2rem 0
24 | letter-spacing -1px
25 | text-shadow 0px 4px 0 rgba(18, 86, 136, 0.11)
26 | a
27 | color blue
28 | text-decoration none
29 | &:focus
30 | outline 0
31 |
32 |
33 | // "Instagram-like" webfont
34 |
35 | @font-face {
36 | font-family: 'billabongregular';
37 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot');
38 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot?#iefix') format('embedded-opentype'),
39 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.woff') format('woff'),
40 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.ttf') format('truetype'),
41 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.svg#billabongregular') format('svg');
42 | font-weight: normal;
43 | font-style: normal;
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/05/styles/style.styl:
--------------------------------------------------------------------------------
1 | @import '_normalize.styl'
2 | @import '_typography.styl'
3 | @import '_animations.styl'
4 |
5 | body
6 | background offwhite
7 |
8 | .photo-grid
9 | display flex
10 | flex-wrap wrap
11 | max-width 1200px
12 | margin 0 auto
13 |
14 | .grid-figure
15 | flex-basis calc(33.333% - 4rem)
16 | flex-grow 1
17 | flex-shrink 0
18 | margin 0 2rem 2rem 2rem
19 | padding 2rem
20 | border 1px solid lightgray
21 | background white
22 | box-shadow 0 0 0 5px rgba(0,0,0,0.03);
23 | position relative
24 |
25 | .single-photo
26 | @extend .grid-figure
27 | max-width 900px
28 | margin 0 auto
29 | display flex
30 | background white
31 | .grid-figure
32 | box-shadow none
33 | margin 0 2rem 0 0
34 | border 0
35 | padding 0
36 | flex 1 0 60%
37 | max-width 60%
38 | .comments
39 | flex 1 0 40%
40 | max-width 40%
41 | .grid-photo
42 | width 100%
43 | margin 0
44 |
45 | .grid-photo
46 | width calc(100% + 4rem)
47 | margin-left -2rem
48 | margin-top -2rem
49 | max-width none
50 |
51 | // Comments
52 | .remove-comment
53 | background none
54 | border 0
55 | line-height 1
56 | opacity 0
57 | &:hover
58 | color red
59 |
60 | .comment
61 | border-bottom 1px solid lightgrey
62 | padding 0.5rem 0
63 | p
64 | font-size 1.2rem
65 | margin 0
66 | strong
67 | color blue
68 | margin-right 5px
69 | &:hover
70 | .remove-comment
71 | opacity 1
72 |
73 | .comment-form
74 | input, textarea
75 | width 100%
76 | border 0
77 | font-size 1.3rem
78 | padding 1rem 0
79 | border-bottom 1px solid lightgrey
80 | outline none
81 | resize vertical
82 |
83 |
84 | .grid-photo-wrap
85 | position relative
86 |
87 | .likes-heart
88 | background url(http://f.cl.ly/items/3Y373q2Q3J3Y1j203n0m/Bitmap-3.png) center no-repeat
89 | background-size contain
90 | font-size 2rem
91 | padding 1rem
92 | position absolute
93 | color blue
94 | left 50%
95 | top 50%
96 | pointer-events none
97 |
98 |
99 | /*
100 | Buttons
101 | */
102 |
103 | .control-buttons
104 | display flex
105 | justify-content space-between
106 |
107 | button, .button
108 | border 2px solid lighten(grey, 90%)
109 | background none
110 | flex-basis 48%
111 | display inline-block
112 | line-height 2
113 | text-decoration none
114 | padding 5px
115 | text-align center
116 | font-size 15px
117 | color blue
118 | transition all 0.2s
119 | box-sizing padding-box
120 | &:hover, &:focus
121 | border-color blue
122 | outline 0
123 |
124 | /*
125 | Cowboy style speech bubble - you should probably use an SVG for this if you are doing more icons.
126 | */
127 | .speech-bubble
128 | size = 1.25rem
129 | width size * 1.2
130 | height size
131 | background blue
132 | display inline-block
133 | border-radius 50%
134 | position relative
135 | &:after
136 | display inline-block
137 | position absolute
138 | content ''
139 | width: 0;
140 | height: 0;
141 | border-style: solid;
142 | border-width: 0 size size 0
143 | border-color: transparent blue transparent transparent
144 | top 30%
145 | left 0
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/06/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/06/.DS_Store
--------------------------------------------------------------------------------
/06/components/Main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | const Main = React.createClass({
5 | render() {
6 | return (
7 |
8 |
9 | Reduxstagram
10 |
11 | {React.cloneElement(this.props.children, this.props)}
12 |
13 | )
14 | }
15 | });
16 |
17 | export default Main;
18 |
--------------------------------------------------------------------------------
/06/components/PhotoGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const PhotoGrid = React.createClass({
4 | render() {
5 | return (
6 |
7 | I'm the photo grid
8 |
9 | )
10 | }
11 | });
12 |
13 | export default PhotoGrid;
14 |
--------------------------------------------------------------------------------
/06/components/Single.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Single = React.createClass({
4 | render() {
5 | return (
6 |
7 | I'm the single
8 |
9 | )
10 | }
11 | });
12 |
13 | export default Single;
14 |
--------------------------------------------------------------------------------
/06/data/config.js:
--------------------------------------------------------------------------------
1 | import Raven from 'raven-js';
2 |
3 | const sentry_key = 'cb55d4f05cd443ce82303222f77ef5e0';
4 | const sentry_app = '61499';
5 | export const sentry_url = `https://${sentry_key}@app.getsentry.com/${sentry_app}`;
6 |
7 | export function logException(ex, context) {
8 | Raven.captureException(ex, {
9 | extra: context
10 | });
11 | /*eslint no-console:0*/
12 | window && window.console && console.error && console.error(ex);
13 | }
14 |
--------------------------------------------------------------------------------
/06/data/posts.js:
--------------------------------------------------------------------------------
1 | const posts = [
2 | {
3 | "code": "BAcyDyQwcXX",
4 | "caption": "Lunch #hamont",
5 | "likes": 56,
6 | "id": "1161022966406956503",
7 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
8 | },
9 | {
10 | "code": "BAcJeJrQca9",
11 | "caption": "Snow! ⛄️🌨❄️ #lifewithsnickers",
12 | "likes": 59,
13 | "id": "1160844458347054781",
14 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
15 | },
16 | {
17 | "code": "BAF_KY4wcRY",
18 | "caption": "Cleaned my office and mounted my recording gear overhead. Stoked for 2016!",
19 | "likes": 79,
20 | "id": "1154606670337393752",
21 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
22 | },
23 | {
24 | "code": "BAPIPRjQce9",
25 | "caption": "Making baby pancakes for one early rising baby. ☕️🍴",
26 | "likes": 47,
27 | "id": "1157179863266871229",
28 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
29 | },
30 | {
31 | "code": "-hZh6IQcfN",
32 | "caption": "New Stickers just came in. I'll do another mailing in a few weeks if you want some. #javascript",
33 | "likes": 66,
34 | "id": "1126293663140399053",
35 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
36 | },
37 | {
38 | "code": "-B3eiIwcYV",
39 | "caption": "Tacos for breakfast. I love you Austin. 🇺🇸",
40 | "likes": 33,
41 | "id": "1117418173361145365",
42 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
43 | },
44 | {
45 | "code": "BAhvZrRwcfu",
46 | "caption": "Tried poke for the first time at @pokehbar. Delicious! It's like a bowl of sushi",
47 | "likes": 30,
48 | "id": "1162418651480049646",
49 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
50 | },
51 | {
52 | "code": "BAAJqbOQcW5",
53 | "caption": "Brunchin'",
54 | "likes": 40,
55 | "id": "1152964002473690553",
56 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
57 | },
58 | {
59 | "code": "_4jHytwcUA",
60 | "caption": "2015 can be summed up with one baby and a many lines of code. And sometimes a coding baby. 👶🏼⌨",
61 | "likes": 62,
62 | "id": "1150824171912152320",
63 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
64 | },
65 | {
66 | "code": "_zbaOlQcbn",
67 | "caption": "Lekker Chocoladeletter",
68 | "likes": 52,
69 | "id": "1149382879529256679",
70 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
71 | },
72 | {
73 | "code": "_rmvQfQce8",
74 | "caption": "Just discovered the #hamont farmers market has a new ramen place! 🍜",
75 | "likes": 35,
76 | "id": "1147180903383025596",
77 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
78 | },
79 | {
80 | "code": "_ep9kiQcVy",
81 | "caption": "⛄️",
82 | "likes": 64,
83 | "id": "1143535906423162226",
84 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
85 | },
86 | {
87 | "code": "_XpJcrwcSn",
88 | "caption": "6 page spread on flexbox in this months netmag!",
89 | "likes": 74,
90 | "id": "1141561999742846119",
91 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
92 | },
93 | {
94 | "code": "_KnU7MwceA",
95 | "caption": "Hanging out in my office waiting for 5:00 beers to come around.",
96 | "likes": 54,
97 | "id": "1137894817632733056",
98 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
99 | },
100 | {
101 | "code": "_HMejJQcY5",
102 | "caption": "Today I learned that a long pull espresso is called a 'lungo'",
103 | "likes": 18,
104 | "id": "1136932306813044281",
105 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
106 | },
107 | {
108 | "code": "_Fq2zmwcaz",
109 | "caption": "Awesome hand lettered gift from @eunibae and the HackerYou crew.",
110 | "likes": 48,
111 | "id": "1136502965197194931",
112 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
113 | },
114 | {
115 | "code": "_A2r0aQcfD",
116 | "caption": "Some serious hardware meet JavaScript hacks going down this week at hackeryou. Excited for demo day!",
117 | "likes": 57,
118 | "id": "1135147611821557699",
119 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
120 | },
121 | {
122 | "code": "-1rhFawccs",
123 | "caption": "Some major audio upgrades coming to my next videos 😍",
124 | "likes": 39,
125 | "id": "1132002270913873708",
126 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
127 | },
128 | {
129 | "code": "-pjx-gQcVi",
130 | "caption": "My baby and me. Thanks to @bearandsparrow for this one.",
131 | "likes": 81,
132 | "id": "1128590547628442978",
133 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
134 | },
135 | {
136 | "code": "-oTZ0zQcWt",
137 | "caption": "It's too early. Send coffee.",
138 | "likes": 81,
139 | "id": "1128237044221461933",
140 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
141 | },
142 | {
143 | "code": "-mxKQoQcQh",
144 | "caption": "They both have figured it out. #lifewithsnickers",
145 | "likes": 47,
146 | "id": "1127804966031967265",
147 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
148 | },
149 | {
150 | "code": "-fasqlQceO",
151 | "caption": "Kaitlin decorated the house for the Christmas. So gezellig! #casabos",
152 | "likes": 46,
153 | "id": "1125735850454402958",
154 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
155 | },
156 | {
157 | "code": "-VBgtGQcSf",
158 | "caption": "Trying the new Hamilton Brewery beer. Big fan.",
159 | "likes": 27,
160 | "id": "1122810327591928991",
161 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
162 | },
163 | {
164 | "code": "-FpTyHQcau",
165 | "caption": "I'm in Austin for a conference and doing some training. Enjoying some local brew with my baby.",
166 | "likes": 82,
167 | "id": "1118481761857291950",
168 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
169 | }
170 | ];
171 |
172 |
173 | export default posts;
174 |
--------------------------------------------------------------------------------
/06/reduxstagram.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { render } from 'react-dom';
4 |
5 | // Import css
6 | import css from './styles/style.styl';
7 |
8 | // Import Components
9 | import Main from './components/Main';
10 | import Single from './components/Single';
11 | import PhotoGrid from './components/PhotoGrid';
12 |
13 | // import react router deps
14 | import { Router, Route, IndexRoute, browserHistory } from 'react-router';
15 |
16 | const router = (
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 |
25 | render(router, document.getElementById('root'));
26 |
--------------------------------------------------------------------------------
/06/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compse } from 'redux';
2 | import { syncHistoryWithStore} from 'react-router-redux';
3 | import { browserHistory } from 'react-router';
4 |
5 | // import the root reducer
6 | import rootReducer from './reducers/index';
7 |
8 | import comments from './data/comments';
9 | import posts from './data/posts';
10 |
11 | // create an object for the default data
12 | const defaultState = {
13 | posts,
14 | comments
15 | };
16 |
17 | const store = createStore(rootReducer, defaultState);
18 |
19 | export const history = syncHistoryWithStore(browserHistory, store);
20 |
21 | export default store;
22 |
--------------------------------------------------------------------------------
/06/styles/_animations.styl:
--------------------------------------------------------------------------------
1 | // offset variable gets tacked for centering in addition to the scaling
2 |
3 | offsets = translateX(-50%) translateY(-50%)
4 | .likes-heart
5 | opacity 0
6 | transition all 0.5s // time to fade out after its done
7 | transform offsets scale(5) // this is the "end state"
8 | display block
9 | &.like-enter
10 | transition all .2s
11 | transform offsets scale(1)
12 | opacity 1
13 | &.like-enter-active
14 | transform offsets scale(5)
15 | .like-leave-active
16 | display none
17 |
18 |
--------------------------------------------------------------------------------
/06/styles/_normalize.styl:
--------------------------------------------------------------------------------
1 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button],/* 1 */
2 | input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
3 |
4 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
5 |
6 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
7 |
8 |
9 | img
10 | max-width 100%
11 |
--------------------------------------------------------------------------------
/06/styles/_typography.styl:
--------------------------------------------------------------------------------
1 | /*
2 | Variables
3 | */
4 |
5 | blue = #125688
6 | offwhite = #fafafa
7 | lightgrey = #EDEEED
8 | lightgray = lightgrey // OH Canada!
9 |
10 | html
11 | font-size 10px
12 | font-family sans-serif
13 |
14 | p
15 | font-size 1.6rem
16 | line-height 1.5
17 |
18 | h1
19 | font-family billabong, 'billabongregular'
20 | text-align center
21 | font-weight 100
22 | font-size 13rem
23 | margin 2rem 0
24 | letter-spacing -1px
25 | text-shadow 0px 4px 0 rgba(18, 86, 136, 0.11)
26 | a
27 | color blue
28 | text-decoration none
29 | &:focus
30 | outline 0
31 |
32 |
33 | // "Instagram-like" webfont
34 |
35 | @font-face {
36 | font-family: 'billabongregular';
37 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot');
38 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot?#iefix') format('embedded-opentype'),
39 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.woff') format('woff'),
40 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.ttf') format('truetype'),
41 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.svg#billabongregular') format('svg');
42 | font-weight: normal;
43 | font-style: normal;
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/06/styles/style.styl:
--------------------------------------------------------------------------------
1 | @import '_normalize.styl'
2 | @import '_typography.styl'
3 | @import '_animations.styl'
4 |
5 | body
6 | background offwhite
7 |
8 | .photo-grid
9 | display flex
10 | flex-wrap wrap
11 | max-width 1200px
12 | margin 0 auto
13 |
14 | .grid-figure
15 | flex-basis calc(33.333% - 4rem)
16 | flex-grow 1
17 | flex-shrink 0
18 | margin 0 2rem 2rem 2rem
19 | padding 2rem
20 | border 1px solid lightgray
21 | background white
22 | box-shadow 0 0 0 5px rgba(0,0,0,0.03);
23 | position relative
24 |
25 | .single-photo
26 | @extend .grid-figure
27 | max-width 900px
28 | margin 0 auto
29 | display flex
30 | background white
31 | .grid-figure
32 | box-shadow none
33 | margin 0 2rem 0 0
34 | border 0
35 | padding 0
36 | flex 1 0 60%
37 | max-width 60%
38 | .comments
39 | flex 1 0 40%
40 | max-width 40%
41 | .grid-photo
42 | width 100%
43 | margin 0
44 |
45 | .grid-photo
46 | width calc(100% + 4rem)
47 | margin-left -2rem
48 | margin-top -2rem
49 | max-width none
50 |
51 | // Comments
52 | .remove-comment
53 | background none
54 | border 0
55 | line-height 1
56 | opacity 0
57 | &:hover
58 | color red
59 |
60 | .comment
61 | border-bottom 1px solid lightgrey
62 | padding 0.5rem 0
63 | p
64 | font-size 1.2rem
65 | margin 0
66 | strong
67 | color blue
68 | margin-right 5px
69 | &:hover
70 | .remove-comment
71 | opacity 1
72 |
73 | .comment-form
74 | input, textarea
75 | width 100%
76 | border 0
77 | font-size 1.3rem
78 | padding 1rem 0
79 | border-bottom 1px solid lightgrey
80 | outline none
81 | resize vertical
82 |
83 |
84 | .grid-photo-wrap
85 | position relative
86 |
87 | .likes-heart
88 | background url(http://f.cl.ly/items/3Y373q2Q3J3Y1j203n0m/Bitmap-3.png) center no-repeat
89 | background-size contain
90 | font-size 2rem
91 | padding 1rem
92 | position absolute
93 | color blue
94 | left 50%
95 | top 50%
96 | pointer-events none
97 |
98 |
99 | /*
100 | Buttons
101 | */
102 |
103 | .control-buttons
104 | display flex
105 | justify-content space-between
106 |
107 | button, .button
108 | border 2px solid lighten(grey, 90%)
109 | background none
110 | flex-basis 48%
111 | display inline-block
112 | line-height 2
113 | text-decoration none
114 | padding 5px
115 | text-align center
116 | font-size 15px
117 | color blue
118 | transition all 0.2s
119 | box-sizing padding-box
120 | &:hover, &:focus
121 | border-color blue
122 | outline 0
123 |
124 | /*
125 | Cowboy style speech bubble - you should probably use an SVG for this if you are doing more icons.
126 | */
127 | .speech-bubble
128 | size = 1.25rem
129 | width size * 1.2
130 | height size
131 | background blue
132 | display inline-block
133 | border-radius 50%
134 | position relative
135 | &:after
136 | display inline-block
137 | position absolute
138 | content ''
139 | width: 0;
140 | height: 0;
141 | border-style: solid;
142 | border-width: 0 size size 0
143 | border-color: transparent blue transparent transparent
144 | top 30%
145 | left 0
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/07/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/07/.DS_Store
--------------------------------------------------------------------------------
/07/actions/actionCreators.js:
--------------------------------------------------------------------------------
1 | // increment
2 | export function increment(index) {
3 | return {
4 | type: 'INCREMENT_LIKES',
5 | index
6 | }
7 | }
8 |
9 | // add comment
10 | export function addComment(postId, author, comment) {
11 | return {
12 | type: 'ADD_COMMENT',
13 | postId,
14 | author,
15 | comment
16 | }
17 | }
18 |
19 | // remove comment
20 |
21 | export function removeComment(postId, i) {
22 | return {
23 | type: 'REMOVE_COMMENT',
24 | i,
25 | postId
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/07/components/Main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | const Main = React.createClass({
5 | render() {
6 | return (
7 |
8 |
9 | Reduxstagram
10 |
11 | {React.cloneElement(this.props.children, this.props)}
12 |
13 | )
14 | }
15 | });
16 |
17 | export default Main;
18 |
--------------------------------------------------------------------------------
/07/components/PhotoGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const PhotoGrid = React.createClass({
4 | render() {
5 | return (
6 |
7 | I'm the photo grid
8 |
9 | )
10 | }
11 | });
12 |
13 | export default PhotoGrid;
14 |
--------------------------------------------------------------------------------
/07/components/Single.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Single = React.createClass({
4 | render() {
5 | return (
6 |
7 | I'm the single
8 |
9 | )
10 | }
11 | });
12 |
13 | export default Single;
14 |
--------------------------------------------------------------------------------
/07/data/config.js:
--------------------------------------------------------------------------------
1 | import Raven from 'raven-js';
2 |
3 | const sentry_key = 'cb55d4f05cd443ce82303222f77ef5e0';
4 | const sentry_app = '61499';
5 | export const sentry_url = `https://${sentry_key}@app.getsentry.com/${sentry_app}`;
6 |
7 | export function logException(ex, context) {
8 | Raven.captureException(ex, {
9 | extra: context
10 | });
11 | /*eslint no-console:0*/
12 | window && window.console && console.error && console.error(ex);
13 | }
14 |
--------------------------------------------------------------------------------
/07/data/posts.js:
--------------------------------------------------------------------------------
1 | const posts = [
2 | {
3 | "code": "BAcyDyQwcXX",
4 | "caption": "Lunch #hamont",
5 | "likes": 56,
6 | "id": "1161022966406956503",
7 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
8 | },
9 | {
10 | "code": "BAcJeJrQca9",
11 | "caption": "Snow! ⛄️🌨❄️ #lifewithsnickers",
12 | "likes": 59,
13 | "id": "1160844458347054781",
14 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
15 | },
16 | {
17 | "code": "BAF_KY4wcRY",
18 | "caption": "Cleaned my office and mounted my recording gear overhead. Stoked for 2016!",
19 | "likes": 79,
20 | "id": "1154606670337393752",
21 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
22 | },
23 | {
24 | "code": "BAPIPRjQce9",
25 | "caption": "Making baby pancakes for one early rising baby. ☕️🍴",
26 | "likes": 47,
27 | "id": "1157179863266871229",
28 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
29 | },
30 | {
31 | "code": "-hZh6IQcfN",
32 | "caption": "New Stickers just came in. I'll do another mailing in a few weeks if you want some. #javascript",
33 | "likes": 66,
34 | "id": "1126293663140399053",
35 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
36 | },
37 | {
38 | "code": "-B3eiIwcYV",
39 | "caption": "Tacos for breakfast. I love you Austin. 🇺🇸",
40 | "likes": 33,
41 | "id": "1117418173361145365",
42 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
43 | },
44 | {
45 | "code": "BAhvZrRwcfu",
46 | "caption": "Tried poke for the first time at @pokehbar. Delicious! It's like a bowl of sushi",
47 | "likes": 30,
48 | "id": "1162418651480049646",
49 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
50 | },
51 | {
52 | "code": "BAAJqbOQcW5",
53 | "caption": "Brunchin'",
54 | "likes": 40,
55 | "id": "1152964002473690553",
56 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
57 | },
58 | {
59 | "code": "_4jHytwcUA",
60 | "caption": "2015 can be summed up with one baby and a many lines of code. And sometimes a coding baby. 👶🏼⌨",
61 | "likes": 62,
62 | "id": "1150824171912152320",
63 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
64 | },
65 | {
66 | "code": "_zbaOlQcbn",
67 | "caption": "Lekker Chocoladeletter",
68 | "likes": 52,
69 | "id": "1149382879529256679",
70 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
71 | },
72 | {
73 | "code": "_rmvQfQce8",
74 | "caption": "Just discovered the #hamont farmers market has a new ramen place! 🍜",
75 | "likes": 35,
76 | "id": "1147180903383025596",
77 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
78 | },
79 | {
80 | "code": "_ep9kiQcVy",
81 | "caption": "⛄️",
82 | "likes": 64,
83 | "id": "1143535906423162226",
84 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
85 | },
86 | {
87 | "code": "_XpJcrwcSn",
88 | "caption": "6 page spread on flexbox in this months netmag!",
89 | "likes": 74,
90 | "id": "1141561999742846119",
91 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
92 | },
93 | {
94 | "code": "_KnU7MwceA",
95 | "caption": "Hanging out in my office waiting for 5:00 beers to come around.",
96 | "likes": 54,
97 | "id": "1137894817632733056",
98 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
99 | },
100 | {
101 | "code": "_HMejJQcY5",
102 | "caption": "Today I learned that a long pull espresso is called a 'lungo'",
103 | "likes": 18,
104 | "id": "1136932306813044281",
105 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
106 | },
107 | {
108 | "code": "_Fq2zmwcaz",
109 | "caption": "Awesome hand lettered gift from @eunibae and the HackerYou crew.",
110 | "likes": 48,
111 | "id": "1136502965197194931",
112 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
113 | },
114 | {
115 | "code": "_A2r0aQcfD",
116 | "caption": "Some serious hardware meet JavaScript hacks going down this week at hackeryou. Excited for demo day!",
117 | "likes": 57,
118 | "id": "1135147611821557699",
119 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
120 | },
121 | {
122 | "code": "-1rhFawccs",
123 | "caption": "Some major audio upgrades coming to my next videos 😍",
124 | "likes": 39,
125 | "id": "1132002270913873708",
126 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
127 | },
128 | {
129 | "code": "-pjx-gQcVi",
130 | "caption": "My baby and me. Thanks to @bearandsparrow for this one.",
131 | "likes": 81,
132 | "id": "1128590547628442978",
133 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
134 | },
135 | {
136 | "code": "-oTZ0zQcWt",
137 | "caption": "It's too early. Send coffee.",
138 | "likes": 81,
139 | "id": "1128237044221461933",
140 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
141 | },
142 | {
143 | "code": "-mxKQoQcQh",
144 | "caption": "They both have figured it out. #lifewithsnickers",
145 | "likes": 47,
146 | "id": "1127804966031967265",
147 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
148 | },
149 | {
150 | "code": "-fasqlQceO",
151 | "caption": "Kaitlin decorated the house for the Christmas. So gezellig! #casabos",
152 | "likes": 46,
153 | "id": "1125735850454402958",
154 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
155 | },
156 | {
157 | "code": "-VBgtGQcSf",
158 | "caption": "Trying the new Hamilton Brewery beer. Big fan.",
159 | "likes": 27,
160 | "id": "1122810327591928991",
161 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
162 | },
163 | {
164 | "code": "-FpTyHQcau",
165 | "caption": "I'm in Austin for a conference and doing some training. Enjoying some local brew with my baby.",
166 | "likes": 82,
167 | "id": "1118481761857291950",
168 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
169 | }
170 | ];
171 |
172 |
173 | export default posts;
174 |
--------------------------------------------------------------------------------
/07/reduxstagram.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { render } from 'react-dom';
4 |
5 | // Import css
6 | import css from './styles/style.styl';
7 |
8 | // Import Components
9 | import Main from './components/Main';
10 | import Single from './components/Single';
11 | import PhotoGrid from './components/PhotoGrid';
12 |
13 | // import react router deps
14 | import { Router, Route, IndexRoute, browserHistory } from 'react-router';
15 |
16 | const router = (
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 |
25 | render(router, document.getElementById('root'));
26 |
--------------------------------------------------------------------------------
/07/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compse } from 'redux';
2 | import { syncHistoryWithStore} from 'react-router-redux';
3 | import { browserHistory } from 'react-router';
4 |
5 | // import the root reducer
6 | import rootReducer from './reducers/index';
7 |
8 | import comments from './data/comments';
9 | import posts from './data/posts';
10 |
11 | // create an object for the default data
12 | const defaultState = {
13 | posts,
14 | comments
15 | };
16 |
17 | const store = createStore(rootReducer, defaultState);
18 |
19 | export const history = syncHistoryWithStore(browserHistory, store);
20 |
21 | export default store;
22 |
--------------------------------------------------------------------------------
/07/styles/_animations.styl:
--------------------------------------------------------------------------------
1 | // offset variable gets tacked for centering in addition to the scaling
2 |
3 | offsets = translateX(-50%) translateY(-50%)
4 | .likes-heart
5 | opacity 0
6 | transition all 0.5s // time to fade out after its done
7 | transform offsets scale(5) // this is the "end state"
8 | display block
9 | &.like-enter
10 | transition all .2s
11 | transform offsets scale(1)
12 | opacity 1
13 | &.like-enter-active
14 | transform offsets scale(5)
15 | .like-leave-active
16 | display none
17 |
18 |
--------------------------------------------------------------------------------
/07/styles/_normalize.styl:
--------------------------------------------------------------------------------
1 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button],/* 1 */
2 | input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
3 |
4 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
5 |
6 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
7 |
8 |
9 | img
10 | max-width 100%
11 |
--------------------------------------------------------------------------------
/07/styles/_typography.styl:
--------------------------------------------------------------------------------
1 | /*
2 | Variables
3 | */
4 |
5 | blue = #125688
6 | offwhite = #fafafa
7 | lightgrey = #EDEEED
8 | lightgray = lightgrey // OH Canada!
9 |
10 | html
11 | font-size 10px
12 | font-family sans-serif
13 |
14 | p
15 | font-size 1.6rem
16 | line-height 1.5
17 |
18 | h1
19 | font-family billabong, 'billabongregular'
20 | text-align center
21 | font-weight 100
22 | font-size 13rem
23 | margin 2rem 0
24 | letter-spacing -1px
25 | text-shadow 0px 4px 0 rgba(18, 86, 136, 0.11)
26 | a
27 | color blue
28 | text-decoration none
29 | &:focus
30 | outline 0
31 |
32 |
33 | // "Instagram-like" webfont
34 |
35 | @font-face {
36 | font-family: 'billabongregular';
37 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot');
38 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot?#iefix') format('embedded-opentype'),
39 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.woff') format('woff'),
40 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.ttf') format('truetype'),
41 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.svg#billabongregular') format('svg');
42 | font-weight: normal;
43 | font-style: normal;
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/07/styles/style.styl:
--------------------------------------------------------------------------------
1 | @import '_normalize.styl'
2 | @import '_typography.styl'
3 | @import '_animations.styl'
4 |
5 | body
6 | background offwhite
7 |
8 | .photo-grid
9 | display flex
10 | flex-wrap wrap
11 | max-width 1200px
12 | margin 0 auto
13 |
14 | .grid-figure
15 | flex-basis calc(33.333% - 4rem)
16 | flex-grow 1
17 | flex-shrink 0
18 | margin 0 2rem 2rem 2rem
19 | padding 2rem
20 | border 1px solid lightgray
21 | background white
22 | box-shadow 0 0 0 5px rgba(0,0,0,0.03);
23 | position relative
24 |
25 | .single-photo
26 | @extend .grid-figure
27 | max-width 900px
28 | margin 0 auto
29 | display flex
30 | background white
31 | .grid-figure
32 | box-shadow none
33 | margin 0 2rem 0 0
34 | border 0
35 | padding 0
36 | flex 1 0 60%
37 | max-width 60%
38 | .comments
39 | flex 1 0 40%
40 | max-width 40%
41 | .grid-photo
42 | width 100%
43 | margin 0
44 |
45 | .grid-photo
46 | width calc(100% + 4rem)
47 | margin-left -2rem
48 | margin-top -2rem
49 | max-width none
50 |
51 | // Comments
52 | .remove-comment
53 | background none
54 | border 0
55 | line-height 1
56 | opacity 0
57 | &:hover
58 | color red
59 |
60 | .comment
61 | border-bottom 1px solid lightgrey
62 | padding 0.5rem 0
63 | p
64 | font-size 1.2rem
65 | margin 0
66 | strong
67 | color blue
68 | margin-right 5px
69 | &:hover
70 | .remove-comment
71 | opacity 1
72 |
73 | .comment-form
74 | input, textarea
75 | width 100%
76 | border 0
77 | font-size 1.3rem
78 | padding 1rem 0
79 | border-bottom 1px solid lightgrey
80 | outline none
81 | resize vertical
82 |
83 |
84 | .grid-photo-wrap
85 | position relative
86 |
87 | .likes-heart
88 | background url(http://f.cl.ly/items/3Y373q2Q3J3Y1j203n0m/Bitmap-3.png) center no-repeat
89 | background-size contain
90 | font-size 2rem
91 | padding 1rem
92 | position absolute
93 | color blue
94 | left 50%
95 | top 50%
96 | pointer-events none
97 |
98 |
99 | /*
100 | Buttons
101 | */
102 |
103 | .control-buttons
104 | display flex
105 | justify-content space-between
106 |
107 | button, .button
108 | border 2px solid lighten(grey, 90%)
109 | background none
110 | flex-basis 48%
111 | display inline-block
112 | line-height 2
113 | text-decoration none
114 | padding 5px
115 | text-align center
116 | font-size 15px
117 | color blue
118 | transition all 0.2s
119 | box-sizing padding-box
120 | &:hover, &:focus
121 | border-color blue
122 | outline 0
123 |
124 | /*
125 | Cowboy style speech bubble - you should probably use an SVG for this if you are doing more icons.
126 | */
127 | .speech-bubble
128 | size = 1.25rem
129 | width size * 1.2
130 | height size
131 | background blue
132 | display inline-block
133 | border-radius 50%
134 | position relative
135 | &:after
136 | display inline-block
137 | position absolute
138 | content ''
139 | width: 0;
140 | height: 0;
141 | border-style: solid;
142 | border-width: 0 size size 0
143 | border-color: transparent blue transparent transparent
144 | top 30%
145 | left 0
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/08/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/08/.DS_Store
--------------------------------------------------------------------------------
/08/actions/actionCreators.js:
--------------------------------------------------------------------------------
1 | // increment
2 | export function increment(index) {
3 | return {
4 | type: 'INCREMENT_LIKES',
5 | index
6 | }
7 | }
8 |
9 | // add comment
10 | export function addComment(postId, author, comment) {
11 | return {
12 | type: 'ADD_COMMENT',
13 | postId,
14 | author,
15 | comment
16 | }
17 | }
18 |
19 | // remove comment
20 |
21 | export function removeComment(postId, i) {
22 | return {
23 | type: 'REMOVE_COMMENT',
24 | i,
25 | postId
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/08/components/Main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | const Main = React.createClass({
5 | render() {
6 | return (
7 |
8 |
9 | Reduxstagram
10 |
11 | {React.cloneElement(this.props.children, this.props)}
12 |
13 | )
14 | }
15 | });
16 |
17 | export default Main;
18 |
--------------------------------------------------------------------------------
/08/components/PhotoGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const PhotoGrid = React.createClass({
4 | render() {
5 | return (
6 |
7 | I'm the photo grid
8 |
9 | )
10 | }
11 | });
12 |
13 | export default PhotoGrid;
14 |
--------------------------------------------------------------------------------
/08/components/Single.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Single = React.createClass({
4 | render() {
5 | return (
6 |
7 | I'm the single
8 |
9 | )
10 | }
11 | });
12 |
13 | export default Single;
14 |
--------------------------------------------------------------------------------
/08/data/config.js:
--------------------------------------------------------------------------------
1 | import Raven from 'raven-js';
2 |
3 | const sentry_key = 'cb55d4f05cd443ce82303222f77ef5e0';
4 | const sentry_app = '61499';
5 | export const sentry_url = `https://${sentry_key}@app.getsentry.com/${sentry_app}`;
6 |
7 | export function logException(ex, context) {
8 | Raven.captureException(ex, {
9 | extra: context
10 | });
11 | /*eslint no-console:0*/
12 | window && window.console && console.error && console.error(ex);
13 | }
14 |
--------------------------------------------------------------------------------
/08/data/posts.js:
--------------------------------------------------------------------------------
1 | const posts = [
2 | {
3 | "code": "BAcyDyQwcXX",
4 | "caption": "Lunch #hamont",
5 | "likes": 56,
6 | "id": "1161022966406956503",
7 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
8 | },
9 | {
10 | "code": "BAcJeJrQca9",
11 | "caption": "Snow! ⛄️🌨❄️ #lifewithsnickers",
12 | "likes": 59,
13 | "id": "1160844458347054781",
14 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
15 | },
16 | {
17 | "code": "BAF_KY4wcRY",
18 | "caption": "Cleaned my office and mounted my recording gear overhead. Stoked for 2016!",
19 | "likes": 79,
20 | "id": "1154606670337393752",
21 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
22 | },
23 | {
24 | "code": "BAPIPRjQce9",
25 | "caption": "Making baby pancakes for one early rising baby. ☕️🍴",
26 | "likes": 47,
27 | "id": "1157179863266871229",
28 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
29 | },
30 | {
31 | "code": "-hZh6IQcfN",
32 | "caption": "New Stickers just came in. I'll do another mailing in a few weeks if you want some. #javascript",
33 | "likes": 66,
34 | "id": "1126293663140399053",
35 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
36 | },
37 | {
38 | "code": "-B3eiIwcYV",
39 | "caption": "Tacos for breakfast. I love you Austin. 🇺🇸",
40 | "likes": 33,
41 | "id": "1117418173361145365",
42 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
43 | },
44 | {
45 | "code": "BAhvZrRwcfu",
46 | "caption": "Tried poke for the first time at @pokehbar. Delicious! It's like a bowl of sushi",
47 | "likes": 30,
48 | "id": "1162418651480049646",
49 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
50 | },
51 | {
52 | "code": "BAAJqbOQcW5",
53 | "caption": "Brunchin'",
54 | "likes": 40,
55 | "id": "1152964002473690553",
56 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
57 | },
58 | {
59 | "code": "_4jHytwcUA",
60 | "caption": "2015 can be summed up with one baby and a many lines of code. And sometimes a coding baby. 👶🏼⌨",
61 | "likes": 62,
62 | "id": "1150824171912152320",
63 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
64 | },
65 | {
66 | "code": "_zbaOlQcbn",
67 | "caption": "Lekker Chocoladeletter",
68 | "likes": 52,
69 | "id": "1149382879529256679",
70 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
71 | },
72 | {
73 | "code": "_rmvQfQce8",
74 | "caption": "Just discovered the #hamont farmers market has a new ramen place! 🍜",
75 | "likes": 35,
76 | "id": "1147180903383025596",
77 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
78 | },
79 | {
80 | "code": "_ep9kiQcVy",
81 | "caption": "⛄️",
82 | "likes": 64,
83 | "id": "1143535906423162226",
84 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
85 | },
86 | {
87 | "code": "_XpJcrwcSn",
88 | "caption": "6 page spread on flexbox in this months netmag!",
89 | "likes": 74,
90 | "id": "1141561999742846119",
91 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
92 | },
93 | {
94 | "code": "_KnU7MwceA",
95 | "caption": "Hanging out in my office waiting for 5:00 beers to come around.",
96 | "likes": 54,
97 | "id": "1137894817632733056",
98 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
99 | },
100 | {
101 | "code": "_HMejJQcY5",
102 | "caption": "Today I learned that a long pull espresso is called a 'lungo'",
103 | "likes": 18,
104 | "id": "1136932306813044281",
105 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
106 | },
107 | {
108 | "code": "_Fq2zmwcaz",
109 | "caption": "Awesome hand lettered gift from @eunibae and the HackerYou crew.",
110 | "likes": 48,
111 | "id": "1136502965197194931",
112 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
113 | },
114 | {
115 | "code": "_A2r0aQcfD",
116 | "caption": "Some serious hardware meet JavaScript hacks going down this week at hackeryou. Excited for demo day!",
117 | "likes": 57,
118 | "id": "1135147611821557699",
119 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
120 | },
121 | {
122 | "code": "-1rhFawccs",
123 | "caption": "Some major audio upgrades coming to my next videos 😍",
124 | "likes": 39,
125 | "id": "1132002270913873708",
126 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
127 | },
128 | {
129 | "code": "-pjx-gQcVi",
130 | "caption": "My baby and me. Thanks to @bearandsparrow for this one.",
131 | "likes": 81,
132 | "id": "1128590547628442978",
133 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
134 | },
135 | {
136 | "code": "-oTZ0zQcWt",
137 | "caption": "It's too early. Send coffee.",
138 | "likes": 81,
139 | "id": "1128237044221461933",
140 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
141 | },
142 | {
143 | "code": "-mxKQoQcQh",
144 | "caption": "They both have figured it out. #lifewithsnickers",
145 | "likes": 47,
146 | "id": "1127804966031967265",
147 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
148 | },
149 | {
150 | "code": "-fasqlQceO",
151 | "caption": "Kaitlin decorated the house for the Christmas. So gezellig! #casabos",
152 | "likes": 46,
153 | "id": "1125735850454402958",
154 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
155 | },
156 | {
157 | "code": "-VBgtGQcSf",
158 | "caption": "Trying the new Hamilton Brewery beer. Big fan.",
159 | "likes": 27,
160 | "id": "1122810327591928991",
161 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
162 | },
163 | {
164 | "code": "-FpTyHQcau",
165 | "caption": "I'm in Austin for a conference and doing some training. Enjoying some local brew with my baby.",
166 | "likes": 82,
167 | "id": "1118481761857291950",
168 | "display_src": `https://picsum.photos/400/400/?image=${Math.floor((Math.random() * 85))}`,
169 | }
170 | ];
171 |
172 |
173 | export default posts;
174 |
--------------------------------------------------------------------------------
/08/reducers/comments.js:
--------------------------------------------------------------------------------
1 | function comments(state = [], action) {
2 | console.log(state, action);
3 | return state;
4 | }
5 |
6 | export default comments;
7 |
--------------------------------------------------------------------------------
/08/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer } from 'react-router-redux';
3 |
4 | import posts from './posts';
5 | import comments from './comments';
6 |
7 | const rootReducer = combineReducers({posts, comments, router: routerReducer });
8 |
9 | export default rootReducer;
10 |
--------------------------------------------------------------------------------
/08/reducers/posts.js:
--------------------------------------------------------------------------------
1 | // a reducer takes in two things:
2 |
3 | // 1. the action (info about what happened)
4 | // 2. copy of current state
5 |
6 | function posts(state = [], action) {
7 | console.log(state, action);
8 | return state;
9 | }
10 |
11 | export default posts;
12 |
--------------------------------------------------------------------------------
/08/reduxstagram.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { render } from 'react-dom';
4 |
5 | // Import css
6 | import css from './styles/style.styl';
7 |
8 | // Import Components
9 | import Main from './components/Main';
10 | import Single from './components/Single';
11 | import PhotoGrid from './components/PhotoGrid';
12 |
13 | // import react router deps
14 | import { Router, Route, IndexRoute, browserHistory } from 'react-router';
15 |
16 | const router = (
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 |
25 | render(router, document.getElementById('root'));
26 |
--------------------------------------------------------------------------------
/08/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compse } from 'redux';
2 | import { syncHistoryWithStore} from 'react-router-redux';
3 | import { browserHistory } from 'react-router';
4 |
5 | // import the root reducer
6 | import rootReducer from './reducers/index';
7 |
8 | import comments from './data/comments';
9 | import posts from './data/posts';
10 |
11 | // create an object for the default data
12 | const defaultState = {
13 | posts,
14 | comments
15 | };
16 |
17 | const store = createStore(rootReducer, defaultState);
18 |
19 | export const history = syncHistoryWithStore(browserHistory, store);
20 |
21 | export default store;
22 |
--------------------------------------------------------------------------------
/08/styles/_animations.styl:
--------------------------------------------------------------------------------
1 | // offset variable gets tacked for centering in addition to the scaling
2 |
3 | offsets = translateX(-50%) translateY(-50%)
4 | .likes-heart
5 | opacity 0
6 | transition all 0.5s // time to fade out after its done
7 | transform offsets scale(5) // this is the "end state"
8 | display block
9 | &.like-enter
10 | transition all .2s
11 | transform offsets scale(1)
12 | opacity 1
13 | &.like-enter-active
14 | transform offsets scale(5)
15 | .like-leave-active
16 | display none
17 |
18 |
--------------------------------------------------------------------------------
/08/styles/_normalize.styl:
--------------------------------------------------------------------------------
1 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button],/* 1 */
2 | input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
3 |
4 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
5 |
6 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
7 |
8 |
9 | img
10 | max-width 100%
11 |
--------------------------------------------------------------------------------
/08/styles/_typography.styl:
--------------------------------------------------------------------------------
1 | /*
2 | Variables
3 | */
4 |
5 | blue = #125688
6 | offwhite = #fafafa
7 | lightgrey = #EDEEED
8 | lightgray = lightgrey // OH Canada!
9 |
10 | html
11 | font-size 10px
12 | font-family sans-serif
13 |
14 | p
15 | font-size 1.6rem
16 | line-height 1.5
17 |
18 | h1
19 | font-family billabong, 'billabongregular'
20 | text-align center
21 | font-weight 100
22 | font-size 13rem
23 | margin 2rem 0
24 | letter-spacing -1px
25 | text-shadow 0px 4px 0 rgba(18, 86, 136, 0.11)
26 | a
27 | color blue
28 | text-decoration none
29 | &:focus
30 | outline 0
31 |
32 |
33 | // "Instagram-like" webfont
34 |
35 | @font-face {
36 | font-family: 'billabongregular';
37 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot');
38 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot?#iefix') format('embedded-opentype'),
39 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.woff') format('woff'),
40 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.ttf') format('truetype'),
41 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.svg#billabongregular') format('svg');
42 | font-weight: normal;
43 | font-style: normal;
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/08/styles/style.styl:
--------------------------------------------------------------------------------
1 | @import '_normalize.styl'
2 | @import '_typography.styl'
3 | @import '_animations.styl'
4 |
5 | body
6 | background offwhite
7 |
8 | .photo-grid
9 | display flex
10 | flex-wrap wrap
11 | max-width 1200px
12 | margin 0 auto
13 |
14 | .grid-figure
15 | flex-basis calc(33.333% - 4rem)
16 | flex-grow 1
17 | flex-shrink 0
18 | margin 0 2rem 2rem 2rem
19 | padding 2rem
20 | border 1px solid lightgray
21 | background white
22 | box-shadow 0 0 0 5px rgba(0,0,0,0.03);
23 | position relative
24 |
25 | .single-photo
26 | @extend .grid-figure
27 | max-width 900px
28 | margin 0 auto
29 | display flex
30 | background white
31 | .grid-figure
32 | box-shadow none
33 | margin 0 2rem 0 0
34 | border 0
35 | padding 0
36 | flex 1 0 60%
37 | max-width 60%
38 | .comments
39 | flex 1 0 40%
40 | max-width 40%
41 | .grid-photo
42 | width 100%
43 | margin 0
44 |
45 | .grid-photo
46 | width calc(100% + 4rem)
47 | margin-left -2rem
48 | margin-top -2rem
49 | max-width none
50 |
51 | // Comments
52 | .remove-comment
53 | background none
54 | border 0
55 | line-height 1
56 | opacity 0
57 | &:hover
58 | color red
59 |
60 | .comment
61 | border-bottom 1px solid lightgrey
62 | padding 0.5rem 0
63 | p
64 | font-size 1.2rem
65 | margin 0
66 | strong
67 | color blue
68 | margin-right 5px
69 | &:hover
70 | .remove-comment
71 | opacity 1
72 |
73 | .comment-form
74 | input, textarea
75 | width 100%
76 | border 0
77 | font-size 1.3rem
78 | padding 1rem 0
79 | border-bottom 1px solid lightgrey
80 | outline none
81 | resize vertical
82 |
83 |
84 | .grid-photo-wrap
85 | position relative
86 |
87 | .likes-heart
88 | background url(http://f.cl.ly/items/3Y373q2Q3J3Y1j203n0m/Bitmap-3.png) center no-repeat
89 | background-size contain
90 | font-size 2rem
91 | padding 1rem
92 | position absolute
93 | color blue
94 | left 50%
95 | top 50%
96 | pointer-events none
97 |
98 |
99 | /*
100 | Buttons
101 | */
102 |
103 | .control-buttons
104 | display flex
105 | justify-content space-between
106 |
107 | button, .button
108 | border 2px solid lighten(grey, 90%)
109 | background none
110 | flex-basis 48%
111 | display inline-block
112 | line-height 2
113 | text-decoration none
114 | padding 5px
115 | text-align center
116 | font-size 15px
117 | color blue
118 | transition all 0.2s
119 | box-sizing padding-box
120 | &:hover, &:focus
121 | border-color blue
122 | outline 0
123 |
124 | /*
125 | Cowboy style speech bubble - you should probably use an SVG for this if you are doing more icons.
126 | */
127 | .speech-bubble
128 | size = 1.25rem
129 | width size * 1.2
130 | height size
131 | background blue
132 | display inline-block
133 | border-radius 50%
134 | position relative
135 | &:after
136 | display inline-block
137 | position absolute
138 | content ''
139 | width: 0;
140 | height: 0;
141 | border-style: solid;
142 | border-width: 0 size size 0
143 | border-color: transparent blue transparent transparent
144 | top 30%
145 | left 0
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/10/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/10/.DS_Store
--------------------------------------------------------------------------------
/10/actions/actionCreators.js:
--------------------------------------------------------------------------------
1 | // increment
2 | export function increment(index) {
3 | return {
4 | type: 'INCREMENT_LIKES',
5 | index
6 | }
7 | }
8 |
9 | // add comment
10 | export function addComment(postId, author, comment) {
11 | return {
12 | type: 'ADD_COMMENT',
13 | postId,
14 | author,
15 | comment
16 | }
17 | }
18 |
19 | // remove comment
20 |
21 | export function removeComment(postId, i) {
22 | return {
23 | type: 'REMOVE_COMMENT',
24 | i,
25 | postId
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/10/components/Main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | const Main = React.createClass({
5 | render() {
6 | return (
7 |
8 |
9 | Reduxstagram
10 |
11 | {React.cloneElement(this.props.children, this.props)}
12 |
13 | )
14 | }
15 | });
16 |
17 | export default Main;
18 |
--------------------------------------------------------------------------------
/10/components/PhotoGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const PhotoGrid = React.createClass({
4 | render() {
5 | return (
6 |
7 | I'm the photo grid
8 |
9 | )
10 | }
11 | });
12 |
13 | export default PhotoGrid;
14 |
--------------------------------------------------------------------------------
/10/components/Single.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Single = React.createClass({
4 | render() {
5 | return (
6 |
7 | I'm the single
8 |
9 | )
10 | }
11 | });
12 |
13 | export default Single;
14 |
--------------------------------------------------------------------------------
/10/data/config.js:
--------------------------------------------------------------------------------
1 | import Raven from 'raven-js';
2 |
3 | const sentry_key = 'cb55d4f05cd443ce82303222f77ef5e0';
4 | const sentry_app = '61499';
5 | export const sentry_url = `https://${sentry_key}@app.getsentry.com/${sentry_app}`;
6 |
7 | export function logException(ex, context) {
8 | Raven.captureException(ex, {
9 | extra: context
10 | });
11 | /*eslint no-console:0*/
12 | window && window.console && console.error && console.error(ex);
13 | }
14 |
--------------------------------------------------------------------------------
/10/reducers/comments.js:
--------------------------------------------------------------------------------
1 | function comments(state = [], action) {
2 | console.log(state, action);
3 | return state;
4 | }
5 |
6 | export default comments;
7 |
--------------------------------------------------------------------------------
/10/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer } from 'react-router-redux';
3 |
4 | import posts from './posts';
5 | import comments from './comments';
6 |
7 | const rootReducer = combineReducers({posts, comments, routing: routerReducer });
8 |
9 | export default rootReducer;
10 |
--------------------------------------------------------------------------------
/10/reducers/posts.js:
--------------------------------------------------------------------------------
1 | // a reducer takes in two things:
2 |
3 | // 1. the action (info about what happened)
4 | // 2. copy of current state
5 |
6 | function posts(state = [], action) {
7 | console.log("The post will change");
8 | console.log(state, action);
9 | return state;
10 | }
11 |
12 | export default posts;
13 |
--------------------------------------------------------------------------------
/10/reduxstagram.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { render } from 'react-dom';
4 |
5 | // Import css
6 | import css from './styles/style.styl';
7 |
8 | // Import Components
9 | import Main from './components/Main';
10 | import Single from './components/Single';
11 | import PhotoGrid from './components/PhotoGrid';
12 |
13 | // import react router deps
14 | import { Router, Route, IndexRoute, browserHistory } from 'react-router';
15 | import { Provider } from 'react-redux';
16 | import store, { history } from './store';
17 |
18 | const router = (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | )
28 |
29 | render(router, document.getElementById('root'));
30 |
--------------------------------------------------------------------------------
/10/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compse } from 'redux';
2 | import { syncHistoryWithStore} from 'react-router-redux';
3 | import { browserHistory } from 'react-router';
4 |
5 | // import the root reducer
6 | import rootReducer from './reducers/index';
7 |
8 | import comments from './data/comments';
9 | import posts from './data/posts';
10 |
11 | // create an object for the default data
12 | const defaultState = {
13 | posts,
14 | comments
15 | };
16 |
17 | const store = createStore(rootReducer, defaultState);
18 |
19 | export const history = syncHistoryWithStore(browserHistory, store);
20 |
21 | export default store;
22 |
--------------------------------------------------------------------------------
/10/styles/_animations.styl:
--------------------------------------------------------------------------------
1 | // offset variable gets tacked for centering in addition to the scaling
2 |
3 | offsets = translateX(-50%) translateY(-50%)
4 | .likes-heart
5 | opacity 0
6 | transition all 0.5s // time to fade out after its done
7 | transform offsets scale(5) // this is the "end state"
8 | display block
9 | &.like-enter
10 | transition all .2s
11 | transform offsets scale(1)
12 | opacity 1
13 | &.like-enter-active
14 | transform offsets scale(5)
15 | .like-leave-active
16 | display none
17 |
18 |
--------------------------------------------------------------------------------
/10/styles/_normalize.styl:
--------------------------------------------------------------------------------
1 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button],/* 1 */
2 | input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
3 |
4 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
5 |
6 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
7 |
8 |
9 | img
10 | max-width 100%
11 |
--------------------------------------------------------------------------------
/10/styles/_typography.styl:
--------------------------------------------------------------------------------
1 | /*
2 | Variables
3 | */
4 |
5 | blue = #125688
6 | offwhite = #fafafa
7 | lightgrey = #EDEEED
8 | lightgray = lightgrey // OH Canada!
9 |
10 | html
11 | font-size 10px
12 | font-family sans-serif
13 |
14 | p
15 | font-size 1.6rem
16 | line-height 1.5
17 |
18 | h1
19 | font-family billabong, 'billabongregular'
20 | text-align center
21 | font-weight 100
22 | font-size 13rem
23 | margin 2rem 0
24 | letter-spacing -1px
25 | text-shadow 0px 4px 0 rgba(18, 86, 136, 0.11)
26 | a
27 | color blue
28 | text-decoration none
29 | &:focus
30 | outline 0
31 |
32 |
33 | // "Instagram-like" webfont
34 |
35 | @font-face {
36 | font-family: 'billabongregular';
37 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot');
38 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot?#iefix') format('embedded-opentype'),
39 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.woff') format('woff'),
40 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.ttf') format('truetype'),
41 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.svg#billabongregular') format('svg');
42 | font-weight: normal;
43 | font-style: normal;
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/10/styles/style.styl:
--------------------------------------------------------------------------------
1 | @import '_normalize.styl'
2 | @import '_typography.styl'
3 | @import '_animations.styl'
4 |
5 | body
6 | background offwhite
7 |
8 | .photo-grid
9 | display flex
10 | flex-wrap wrap
11 | max-width 1200px
12 | margin 0 auto
13 |
14 | .grid-figure
15 | flex-basis calc(33.333% - 4rem)
16 | flex-grow 1
17 | flex-shrink 0
18 | margin 0 2rem 2rem 2rem
19 | padding 2rem
20 | border 1px solid lightgray
21 | background white
22 | box-shadow 0 0 0 5px rgba(0,0,0,0.03);
23 | position relative
24 |
25 | .single-photo
26 | @extend .grid-figure
27 | max-width 900px
28 | margin 0 auto
29 | display flex
30 | background white
31 | .grid-figure
32 | box-shadow none
33 | margin 0 2rem 0 0
34 | border 0
35 | padding 0
36 | flex 1 0 60%
37 | max-width 60%
38 | .comments
39 | flex 1 0 40%
40 | max-width 40%
41 | .grid-photo
42 | width 100%
43 | margin 0
44 |
45 | .grid-photo
46 | width calc(100% + 4rem)
47 | margin-left -2rem
48 | margin-top -2rem
49 | max-width none
50 |
51 | // Comments
52 | .remove-comment
53 | background none
54 | border 0
55 | line-height 1
56 | opacity 0
57 | &:hover
58 | color red
59 |
60 | .comment
61 | border-bottom 1px solid lightgrey
62 | padding 0.5rem 0
63 | p
64 | font-size 1.2rem
65 | margin 0
66 | strong
67 | color blue
68 | margin-right 5px
69 | &:hover
70 | .remove-comment
71 | opacity 1
72 |
73 | .comment-form
74 | input, textarea
75 | width 100%
76 | border 0
77 | font-size 1.3rem
78 | padding 1rem 0
79 | border-bottom 1px solid lightgrey
80 | outline none
81 | resize vertical
82 |
83 |
84 | .grid-photo-wrap
85 | position relative
86 |
87 | .likes-heart
88 | background url(http://f.cl.ly/items/3Y373q2Q3J3Y1j203n0m/Bitmap-3.png) center no-repeat
89 | background-size contain
90 | font-size 2rem
91 | padding 1rem
92 | position absolute
93 | color blue
94 | left 50%
95 | top 50%
96 | pointer-events none
97 |
98 |
99 | /*
100 | Buttons
101 | */
102 |
103 | .control-buttons
104 | display flex
105 | justify-content space-between
106 |
107 | button, .button
108 | border 2px solid lighten(grey, 90%)
109 | background none
110 | flex-basis 48%
111 | display inline-block
112 | line-height 2
113 | text-decoration none
114 | padding 5px
115 | text-align center
116 | font-size 15px
117 | color blue
118 | transition all 0.2s
119 | box-sizing padding-box
120 | &:hover, &:focus
121 | border-color blue
122 | outline 0
123 |
124 | /*
125 | Cowboy style speech bubble - you should probably use an SVG for this if you are doing more icons.
126 | */
127 | .speech-bubble
128 | size = 1.25rem
129 | width size * 1.2
130 | height size
131 | background blue
132 | display inline-block
133 | border-radius 50%
134 | position relative
135 | &:after
136 | display inline-block
137 | position absolute
138 | content ''
139 | width: 0;
140 | height: 0;
141 | border-style: solid;
142 | border-width: 0 size size 0
143 | border-color: transparent blue transparent transparent
144 | top 30%
145 | left 0
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/13/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/13/.DS_Store
--------------------------------------------------------------------------------
/13/actions/actionCreators.js:
--------------------------------------------------------------------------------
1 | // increment
2 | export function increment(index) {
3 | return {
4 | type: 'INCREMENT_LIKES',
5 | index
6 | }
7 | }
8 |
9 | // add comment
10 | export function addComment(postId, author, comment) {
11 | return {
12 | type: 'ADD_COMMENT',
13 | postId,
14 | author,
15 | comment
16 | }
17 | }
18 |
19 | // remove comment
20 |
21 | export function removeComment(postId, i) {
22 | return {
23 | type: 'REMOVE_COMMENT',
24 | i,
25 | postId
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/13/components/App.js:
--------------------------------------------------------------------------------
1 | import { bindActionCreators } from 'redux';
2 | import { connect } from 'react-redux';
3 | import * as actionCreators from '../actions/actionCreators';
4 | import Main from './Main';
5 |
6 | function mapStateToProps(state) {
7 | return {
8 | posts: state.posts,
9 | comments: state.comments
10 | }
11 | }
12 |
13 | function mapDispachToProps(dispatch) {
14 | return bindActionCreators(actionCreators, dispatch);
15 | }
16 |
17 | const App = connect(mapStateToProps, mapDispachToProps)(Main);
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/13/components/Comments.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/13/components/Comments.js
--------------------------------------------------------------------------------
/13/components/Main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | const Main = React.createClass({
5 | render() {
6 | return (
7 |
8 |
9 | Reduxstagram
10 |
11 | {React.cloneElement(this.props.children, this.props)}
12 |
13 | )
14 | }
15 | });
16 |
17 | export default Main;
18 |
--------------------------------------------------------------------------------
/13/components/Photo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 | import CSSTransitionGroup from 'react-addons-css-transition-group';
4 |
5 | const Photo = React.createClass({
6 | render() {
7 | const { post, i, comments } = this.props;
8 | return (
9 |
10 |
11 |
12 |

13 |
14 |
15 |
16 | {post.likes}
17 |
18 |
19 |
20 |
21 |
22 | {post.caption}
23 |
24 |
25 |
26 |
27 |
28 | {comments[post.code] ? comments[post.code].length : 0 }
29 |
30 |
31 |
32 |
33 |
34 |
35 | )
36 | }
37 | });
38 |
39 | export default Photo;
40 |
--------------------------------------------------------------------------------
/13/components/PhotoGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Photo from './Photo';
3 |
4 | const PhotoGrid = React.createClass({
5 | render() {
6 | return (
7 |
8 | {this.props.posts.map((post, i) =>
)}
9 |
10 | )
11 | }
12 | });
13 |
14 | export default PhotoGrid;
15 |
--------------------------------------------------------------------------------
/13/components/Single.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Single = React.createClass({
4 | render() {
5 | return (
6 |
7 | Im the single
8 |
9 | )
10 | }
11 | });
12 |
13 | export default Single;
14 |
--------------------------------------------------------------------------------
/13/data/config.js:
--------------------------------------------------------------------------------
1 | import Raven from 'raven-js';
2 |
3 | const sentry_key = 'cb55d4f05cd443ce82303222f77ef5e0';
4 | const sentry_app = '61499';
5 | export const sentry_url = `https://${sentry_key}@app.getsentry.com/${sentry_app}`;
6 |
7 | export function logException(ex, context) {
8 | Raven.captureException(ex, {
9 | extra: context
10 | });
11 | /*eslint no-console:0*/
12 | window && window.console && console.error && console.error(ex);
13 | }
14 |
--------------------------------------------------------------------------------
/13/reducers/comments.js:
--------------------------------------------------------------------------------
1 | function comments(state = [], action) {
2 | return state;
3 | }
4 |
5 | export default comments;
6 |
--------------------------------------------------------------------------------
/13/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer } from 'react-router-redux';
3 |
4 | import posts from './posts';
5 | import comments from './comments';
6 |
7 | const rootReducer = combineReducers({posts, comments, routing: routerReducer });
8 |
9 | export default rootReducer;
10 |
--------------------------------------------------------------------------------
/13/reducers/posts.js:
--------------------------------------------------------------------------------
1 | // a reducer takes in two things:
2 |
3 | // 1. the action (info about what happened)
4 | // 2. copy of current state
5 |
6 | function posts(state = [], action) {
7 | switch(action.type) {
8 | case 'INCREMENT_LIKES' :
9 | console.log("Incrementing Likes!!");
10 | const i = action.index;
11 | return [
12 | ...state.slice(0,i), // before the one we are updating
13 | {...state[i], likes: state[i].likes + 1},
14 | ...state.slice(i + 1), // after the one we are updating
15 | ]
16 | default:
17 | return state;
18 | }
19 | }
20 |
21 | export default posts;
22 |
--------------------------------------------------------------------------------
/13/reduxstagram.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { render } from 'react-dom';
4 |
5 | // Import css
6 | import css from './styles/style.styl';
7 |
8 | // Import Components
9 | import App from './components/App';
10 | import Single from './components/Single';
11 | import PhotoGrid from './components/PhotoGrid';
12 |
13 | // import react router deps
14 | import { Router, Route, IndexRoute, browserHistory } from 'react-router';
15 | import { Provider } from 'react-redux';
16 | import store, { history } from './store';
17 |
18 | const router = (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | )
28 |
29 | render(router, document.getElementById('root'));
30 |
--------------------------------------------------------------------------------
/13/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compse } from 'redux';
2 | import { syncHistoryWithStore} from 'react-router-redux';
3 | import { browserHistory } from 'react-router';
4 |
5 | // import the root reducer
6 | import rootReducer from './reducers/index';
7 |
8 | import comments from './data/comments';
9 | import posts from './data/posts';
10 |
11 | // create an object for the default data
12 | const defaultState = {
13 | posts,
14 | comments
15 | };
16 |
17 | const store = createStore(rootReducer, defaultState);
18 |
19 | export const history = syncHistoryWithStore(browserHistory, store);
20 |
21 | export default store;
22 |
--------------------------------------------------------------------------------
/13/styles/_animations.styl:
--------------------------------------------------------------------------------
1 | // offset variable gets tacked for centering in addition to the scaling
2 |
3 | offsets = translateX(-50%) translateY(-50%)
4 | .likes-heart
5 | opacity 0
6 | transition all 0.5s // time to fade out after its done
7 | transform offsets scale(5) // this is the "end state"
8 | display block
9 | &.like-enter
10 | transition all .2s
11 | transform offsets scale(1)
12 | opacity 1
13 | &.like-enter-active
14 | transform offsets scale(5)
15 | .like-leave-active
16 | display none
17 |
18 |
--------------------------------------------------------------------------------
/13/styles/_normalize.styl:
--------------------------------------------------------------------------------
1 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button],/* 1 */
2 | input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
3 |
4 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
5 |
6 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
7 |
8 |
9 | img
10 | max-width 100%
11 |
--------------------------------------------------------------------------------
/13/styles/_typography.styl:
--------------------------------------------------------------------------------
1 | /*
2 | Variables
3 | */
4 |
5 | blue = #125688
6 | offwhite = #fafafa
7 | lightgrey = #EDEEED
8 | lightgray = lightgrey // OH Canada!
9 |
10 | html
11 | font-size 10px
12 | font-family sans-serif
13 |
14 | p
15 | font-size 1.6rem
16 | line-height 1.5
17 |
18 | h1
19 | font-family billabong, 'billabongregular'
20 | text-align center
21 | font-weight 100
22 | font-size 13rem
23 | margin 2rem 0
24 | letter-spacing -1px
25 | text-shadow 0px 4px 0 rgba(18, 86, 136, 0.11)
26 | a
27 | color blue
28 | text-decoration none
29 | &:focus
30 | outline 0
31 |
32 |
33 | // "Instagram-like" webfont
34 |
35 | @font-face {
36 | font-family: 'billabongregular';
37 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot');
38 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot?#iefix') format('embedded-opentype'),
39 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.woff') format('woff'),
40 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.ttf') format('truetype'),
41 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.svg#billabongregular') format('svg');
42 | font-weight: normal;
43 | font-style: normal;
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/13/styles/style.styl:
--------------------------------------------------------------------------------
1 | @import '_normalize.styl'
2 | @import '_typography.styl'
3 | @import '_animations.styl'
4 |
5 | body
6 | background offwhite
7 |
8 | .photo-grid
9 | display flex
10 | flex-wrap wrap
11 | max-width 1200px
12 | margin 0 auto
13 |
14 | .grid-figure
15 | flex-basis calc(33.333% - 4rem)
16 | flex-grow 1
17 | flex-shrink 0
18 | margin 0 2rem 2rem 2rem
19 | padding 2rem
20 | border 1px solid lightgray
21 | background white
22 | box-shadow 0 0 0 5px rgba(0,0,0,0.03);
23 | position relative
24 |
25 | .single-photo
26 | @extend .grid-figure
27 | max-width 900px
28 | margin 0 auto
29 | display flex
30 | background white
31 | .grid-figure
32 | box-shadow none
33 | margin 0 2rem 0 0
34 | border 0
35 | padding 0
36 | flex 1 0 60%
37 | max-width 60%
38 | .comments
39 | flex 1 0 40%
40 | max-width 40%
41 | .grid-photo
42 | width 100%
43 | margin 0
44 |
45 | .grid-photo
46 | width calc(100% + 4rem)
47 | margin-left -2rem
48 | margin-top -2rem
49 | max-width none
50 |
51 | // Comments
52 | .remove-comment
53 | background none
54 | border 0
55 | line-height 1
56 | opacity 0
57 | &:hover
58 | color red
59 |
60 | .comment
61 | border-bottom 1px solid lightgrey
62 | padding 0.5rem 0
63 | p
64 | font-size 1.2rem
65 | margin 0
66 | strong
67 | color blue
68 | margin-right 5px
69 | &:hover
70 | .remove-comment
71 | opacity 1
72 |
73 | .comment-form
74 | input, textarea
75 | width 100%
76 | border 0
77 | font-size 1.3rem
78 | padding 1rem 0
79 | border-bottom 1px solid lightgrey
80 | outline none
81 | resize vertical
82 |
83 |
84 | .grid-photo-wrap
85 | position relative
86 |
87 | .likes-heart
88 | background url(http://f.cl.ly/items/3Y373q2Q3J3Y1j203n0m/Bitmap-3.png) center no-repeat
89 | background-size contain
90 | font-size 2rem
91 | padding 1rem
92 | position absolute
93 | color blue
94 | left 50%
95 | top 50%
96 | pointer-events none
97 |
98 |
99 | /*
100 | Buttons
101 | */
102 |
103 | .control-buttons
104 | display flex
105 | justify-content space-between
106 |
107 | button, .button
108 | border 2px solid lighten(grey, 90%)
109 | background none
110 | flex-basis 48%
111 | display inline-block
112 | line-height 2
113 | text-decoration none
114 | padding 5px
115 | text-align center
116 | font-size 15px
117 | color blue
118 | transition all 0.2s
119 | box-sizing padding-box
120 | &:hover, &:focus
121 | border-color blue
122 | outline 0
123 |
124 | /*
125 | Cowboy style speech bubble - you should probably use an SVG for this if you are doing more icons.
126 | */
127 | .speech-bubble
128 | size = 1.25rem
129 | width size * 1.2
130 | height size
131 | background blue
132 | display inline-block
133 | border-radius 50%
134 | position relative
135 | &:after
136 | display inline-block
137 | position absolute
138 | content ''
139 | width: 0;
140 | height: 0;
141 | border-style: solid;
142 | border-width: 0 size size 0
143 | border-color: transparent blue transparent transparent
144 | top 30%
145 | left 0
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/14/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/14/.DS_Store
--------------------------------------------------------------------------------
/14/actions/actionCreators.js:
--------------------------------------------------------------------------------
1 | // increment
2 | export function increment(index) {
3 | return {
4 | type: 'INCREMENT_LIKES',
5 | index
6 | }
7 | }
8 |
9 | // add comment
10 | export function addComment(postId, author, comment) {
11 | return {
12 | type: 'ADD_COMMENT',
13 | postId,
14 | author,
15 | comment
16 | }
17 | }
18 |
19 | // remove comment
20 |
21 | export function removeComment(postId, i) {
22 | return {
23 | type: 'REMOVE_COMMENT',
24 | i,
25 | postId
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/14/components/App.js:
--------------------------------------------------------------------------------
1 | import { bindActionCreators } from 'redux';
2 | import { connect } from 'react-redux';
3 | import * as actionCreators from '../actions/actionCreators';
4 | import Main from './Main';
5 |
6 | function mapStateToProps(state) {
7 | return {
8 | posts: state.posts,
9 | comments: state.comments
10 | }
11 | }
12 |
13 | function mapDispachToProps(dispatch) {
14 | return bindActionCreators(actionCreators, dispatch);
15 | }
16 |
17 | const App = connect(mapStateToProps, mapDispachToProps)(Main);
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/14/components/Comments.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Comments = React.createClass({
4 | renderComment(comment, i) {
5 | return (
6 |
7 |
8 | {comment.user}
9 | {comment.text}
10 |
11 |
12 |
13 | )
14 | },
15 |
16 | render() {
17 | return (
18 |
19 | {this.props.postComments.map(this.renderComment)}
20 |
25 |
26 | )
27 | }
28 | });
29 |
30 | export default Comments;
31 |
--------------------------------------------------------------------------------
/14/components/Main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | const Main = React.createClass({
5 | render() {
6 | return (
7 |
8 |
9 | Reduxstagram
10 |
11 | {React.cloneElement(this.props.children, this.props)}
12 |
13 | )
14 | }
15 | });
16 |
17 | export default Main;
18 |
--------------------------------------------------------------------------------
/14/components/Photo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 | import CSSTransitionGroup from 'react-addons-css-transition-group';
4 |
5 | const Photo = React.createClass({
6 | render() {
7 | const { post, i, comments } = this.props;
8 | return (
9 |
10 |
11 |
12 |

13 |
14 |
15 |
16 | {post.likes}
17 |
18 |
19 |
20 |
21 |
22 | {post.caption}
23 |
24 |
25 |
26 |
27 |
28 | {comments[post.code] ? comments[post.code].length : 0 }
29 |
30 |
31 |
32 |
33 |
34 |
35 | )
36 | }
37 | });
38 |
39 | export default Photo;
40 |
--------------------------------------------------------------------------------
/14/components/PhotoGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Photo from './Photo';
3 |
4 | const PhotoGrid = React.createClass({
5 | render() {
6 | return (
7 |
8 | {this.props.posts.map((post, i) =>
)}
9 |
10 | )
11 | }
12 | });
13 |
14 | export default PhotoGrid;
15 |
--------------------------------------------------------------------------------
/14/components/Single.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Photo from './Photo';
3 | import Comments from './Comments';
4 |
5 | const Single = React.createClass({
6 | render() {
7 | const { postId } = this.props.params;
8 |
9 | const i = this.props.posts.findIndex((post) => post.code === postId);
10 | const post = this.props.posts[i];
11 |
12 | const postComments = this.props.comments[postId] || [];
13 |
14 | return (
15 |
19 | )
20 | }
21 | });
22 |
23 | export default Single;
24 |
--------------------------------------------------------------------------------
/14/data/config.js:
--------------------------------------------------------------------------------
1 | import Raven from 'raven-js';
2 |
3 | const sentry_key = 'cb55d4f05cd443ce82303222f77ef5e0';
4 | const sentry_app = '61499';
5 | export const sentry_url = `https://${sentry_key}@app.getsentry.com/${sentry_app}`;
6 |
7 | export function logException(ex, context) {
8 | Raven.captureException(ex, {
9 | extra: context
10 | });
11 | /*eslint no-console:0*/
12 | window && window.console && console.error && console.error(ex);
13 | }
14 |
--------------------------------------------------------------------------------
/14/reducers/comments.js:
--------------------------------------------------------------------------------
1 | function comments(state = [], action) {
2 | return state;
3 | }
4 |
5 | export default comments;
6 |
--------------------------------------------------------------------------------
/14/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer } from 'react-router-redux';
3 |
4 | import posts from './posts';
5 | import comments from './comments';
6 |
7 | const rootReducer = combineReducers({posts, comments, routing: routerReducer });
8 |
9 | export default rootReducer;
10 |
--------------------------------------------------------------------------------
/14/reducers/posts.js:
--------------------------------------------------------------------------------
1 | // a reducer takes in two things:
2 |
3 | // 1. the action (info about what happened)
4 | // 2. copy of current state
5 |
6 | function posts(state = [], action) {
7 | switch(action.type) {
8 | case 'INCREMENT_LIKES' :
9 | console.log("Incrementing Likes!!");
10 | const i = action.index;
11 | return [
12 | ...state.slice(0,i), // before the one we are updating
13 | {...state[i], likes: state[i].likes + 1},
14 | ...state.slice(i + 1), // after the one we are updating
15 | ]
16 | default:
17 | return state;
18 | }
19 | }
20 |
21 | export default posts;
22 |
--------------------------------------------------------------------------------
/14/reduxstagram.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { render } from 'react-dom';
4 |
5 | // Import css
6 | import css from './styles/style.styl';
7 |
8 | // Import Components
9 | import App from './components/App';
10 | import Single from './components/Single';
11 | import PhotoGrid from './components/PhotoGrid';
12 |
13 | // import react router deps
14 | import { Router, Route, IndexRoute, browserHistory } from 'react-router';
15 | import { Provider } from 'react-redux';
16 | import store, { history } from './store';
17 |
18 | const router = (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | )
28 |
29 | render(router, document.getElementById('root'));
30 |
--------------------------------------------------------------------------------
/14/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compse } from 'redux';
2 | import { syncHistoryWithStore} from 'react-router-redux';
3 | import { browserHistory } from 'react-router';
4 |
5 | // import the root reducer
6 | import rootReducer from './reducers/index';
7 |
8 | import comments from './data/comments';
9 | import posts from './data/posts';
10 |
11 | // create an object for the default data
12 | const defaultState = {
13 | posts,
14 | comments
15 | };
16 |
17 | const store = createStore(rootReducer, defaultState);
18 |
19 | export const history = syncHistoryWithStore(browserHistory, store);
20 |
21 | export default store;
22 |
--------------------------------------------------------------------------------
/14/styles/_animations.styl:
--------------------------------------------------------------------------------
1 | // offset variable gets tacked for centering in addition to the scaling
2 |
3 | offsets = translateX(-50%) translateY(-50%)
4 | .likes-heart
5 | opacity 0
6 | transition all 0.5s // time to fade out after its done
7 | transform offsets scale(5) // this is the "end state"
8 | display block
9 | &.like-enter
10 | transition all .2s
11 | transform offsets scale(1)
12 | opacity 1
13 | &.like-enter-active
14 | transform offsets scale(5)
15 | .like-leave-active
16 | display none
17 |
18 |
--------------------------------------------------------------------------------
/14/styles/_normalize.styl:
--------------------------------------------------------------------------------
1 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button],/* 1 */
2 | input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
3 |
4 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
5 |
6 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
7 |
8 |
9 | img
10 | max-width 100%
11 |
--------------------------------------------------------------------------------
/14/styles/_typography.styl:
--------------------------------------------------------------------------------
1 | /*
2 | Variables
3 | */
4 |
5 | blue = #125688
6 | offwhite = #fafafa
7 | lightgrey = #EDEEED
8 | lightgray = lightgrey // OH Canada!
9 |
10 | html
11 | font-size 10px
12 | font-family sans-serif
13 |
14 | p
15 | font-size 1.6rem
16 | line-height 1.5
17 |
18 | h1
19 | font-family billabong, 'billabongregular'
20 | text-align center
21 | font-weight 100
22 | font-size 13rem
23 | margin 2rem 0
24 | letter-spacing -1px
25 | text-shadow 0px 4px 0 rgba(18, 86, 136, 0.11)
26 | a
27 | color blue
28 | text-decoration none
29 | &:focus
30 | outline 0
31 |
32 |
33 | // "Instagram-like" webfont
34 |
35 | @font-face {
36 | font-family: 'billabongregular';
37 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot');
38 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot?#iefix') format('embedded-opentype'),
39 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.woff') format('woff'),
40 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.ttf') format('truetype'),
41 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.svg#billabongregular') format('svg');
42 | font-weight: normal;
43 | font-style: normal;
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/14/styles/style.styl:
--------------------------------------------------------------------------------
1 | @import '_normalize.styl'
2 | @import '_typography.styl'
3 | @import '_animations.styl'
4 |
5 | body
6 | background offwhite
7 |
8 | .photo-grid
9 | display flex
10 | flex-wrap wrap
11 | max-width 1200px
12 | margin 0 auto
13 |
14 | .grid-figure
15 | flex-basis calc(33.333% - 4rem)
16 | flex-grow 1
17 | flex-shrink 0
18 | margin 0 2rem 2rem 2rem
19 | padding 2rem
20 | border 1px solid lightgray
21 | background white
22 | box-shadow 0 0 0 5px rgba(0,0,0,0.03);
23 | position relative
24 |
25 | .single-photo
26 | @extend .grid-figure
27 | max-width 900px
28 | margin 0 auto
29 | display flex
30 | background white
31 | .grid-figure
32 | box-shadow none
33 | margin 0 2rem 0 0
34 | border 0
35 | padding 0
36 | flex 1 0 60%
37 | max-width 60%
38 | .comments
39 | flex 1 0 40%
40 | max-width 40%
41 | .grid-photo
42 | width 100%
43 | margin 0
44 |
45 | .grid-photo
46 | width calc(100% + 4rem)
47 | margin-left -2rem
48 | margin-top -2rem
49 | max-width none
50 |
51 | // Comments
52 | .remove-comment
53 | background none
54 | border 0
55 | line-height 1
56 | opacity 0
57 | &:hover
58 | color red
59 |
60 | .comment
61 | border-bottom 1px solid lightgrey
62 | padding 0.5rem 0
63 | p
64 | font-size 1.2rem
65 | margin 0
66 | strong
67 | color blue
68 | margin-right 5px
69 | &:hover
70 | .remove-comment
71 | opacity 1
72 |
73 | .comment-form
74 | input, textarea
75 | width 100%
76 | border 0
77 | font-size 1.3rem
78 | padding 1rem 0
79 | border-bottom 1px solid lightgrey
80 | outline none
81 | resize vertical
82 |
83 |
84 | .grid-photo-wrap
85 | position relative
86 |
87 | .likes-heart
88 | background url(http://f.cl.ly/items/3Y373q2Q3J3Y1j203n0m/Bitmap-3.png) center no-repeat
89 | background-size contain
90 | font-size 2rem
91 | padding 1rem
92 | position absolute
93 | color blue
94 | left 50%
95 | top 50%
96 | pointer-events none
97 |
98 |
99 | /*
100 | Buttons
101 | */
102 |
103 | .control-buttons
104 | display flex
105 | justify-content space-between
106 |
107 | button, .button
108 | border 2px solid lighten(grey, 90%)
109 | background none
110 | flex-basis 48%
111 | display inline-block
112 | line-height 2
113 | text-decoration none
114 | padding 5px
115 | text-align center
116 | font-size 15px
117 | color blue
118 | transition all 0.2s
119 | box-sizing padding-box
120 | &:hover, &:focus
121 | border-color blue
122 | outline 0
123 |
124 | /*
125 | Cowboy style speech bubble - you should probably use an SVG for this if you are doing more icons.
126 | */
127 | .speech-bubble
128 | size = 1.25rem
129 | width size * 1.2
130 | height size
131 | background blue
132 | display inline-block
133 | border-radius 50%
134 | position relative
135 | &:after
136 | display inline-block
137 | position absolute
138 | content ''
139 | width: 0;
140 | height: 0;
141 | border-style: solid;
142 | border-width: 0 size size 0
143 | border-color: transparent blue transparent transparent
144 | top 30%
145 | left 0
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/15/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/15/.DS_Store
--------------------------------------------------------------------------------
/15/actions/actionCreators.js:
--------------------------------------------------------------------------------
1 | // increment
2 | export function increment(index) {
3 | return {
4 | type: 'INCREMENT_LIKES',
5 | index
6 | }
7 | }
8 |
9 | // add comment
10 | export function addComment(postId, author, comment) {
11 | return {
12 | type: 'ADD_COMMENT',
13 | postId,
14 | author,
15 | comment
16 | }
17 | }
18 |
19 | // remove comment
20 |
21 | export function removeComment(postId, i) {
22 | return {
23 | type: 'REMOVE_COMMENT',
24 | i,
25 | postId
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/15/components/App.js:
--------------------------------------------------------------------------------
1 | import { bindActionCreators } from 'redux';
2 | import { connect } from 'react-redux';
3 | import * as actionCreators from '../actions/actionCreators';
4 | import Main from './Main';
5 |
6 | function mapStateToProps(state) {
7 | return {
8 | posts: state.posts,
9 | comments: state.comments
10 | }
11 | }
12 |
13 | function mapDispachToProps(dispatch) {
14 | return bindActionCreators(actionCreators, dispatch);
15 | }
16 |
17 | const App = connect(mapStateToProps, mapDispachToProps)(Main);
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/15/components/Comments.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Comments = React.createClass({
4 | renderComment(comment, i) {
5 | return (
6 |
7 |
8 | {comment.user}
9 | {comment.text}
10 |
11 |
12 |
13 | )
14 | },
15 | render() {
16 | return (
17 |
18 | {this.props.postComments.map(this.renderComment)}
19 |
24 |
25 | )
26 | }
27 | });
28 |
29 | export default Comments;
30 |
--------------------------------------------------------------------------------
/15/components/Main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | const Main = React.createClass({
5 | render() {
6 | return (
7 |
8 |
9 | Reduxstagram
10 |
11 | {React.cloneElement(this.props.children, this.props)}
12 |
13 | )
14 | }
15 | });
16 |
17 | export default Main;
18 |
--------------------------------------------------------------------------------
/15/components/Photo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 | import CSSTransitionGroup from 'react-addons-css-transition-group';
4 |
5 | const Photo = React.createClass({
6 | render() {
7 | const { post, i, comments } = this.props;
8 | return (
9 |
10 |
11 |
12 |

13 |
14 |
15 |
16 | {post.likes}
17 |
18 |
19 |
20 |
21 |
22 | {post.caption}
23 |
24 |
25 |
26 |
27 |
28 | {comments[post.code] ? comments[post.code].length : 0 }
29 |
30 |
31 |
32 |
33 |
34 |
35 | )
36 | }
37 | });
38 |
39 | export default Photo;
40 |
--------------------------------------------------------------------------------
/15/components/PhotoGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Photo from './Photo';
3 |
4 | const PhotoGrid = React.createClass({
5 | render() {
6 | return (
7 |
8 | {this.props.posts.map((post, i) =>
)}
9 |
10 | )
11 | }
12 | });
13 |
14 | export default PhotoGrid;
15 |
--------------------------------------------------------------------------------
/15/components/Single.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Photo from './Photo';
3 | import Comments from './Comments';
4 |
5 | const Single = React.createClass({
6 | render() {
7 | const { postId } = this.props.params;
8 |
9 | const i = this.props.posts.findIndex((post) => post.code === postId);
10 | const post = this.props.posts[i];
11 |
12 | const postComments = this.props.comments[postId] || [];
13 |
14 | return (
15 |
19 | )
20 | }
21 | });
22 |
23 | export default Single;
24 |
--------------------------------------------------------------------------------
/15/data/config.js:
--------------------------------------------------------------------------------
1 | import Raven from 'raven-js';
2 |
3 | const sentry_key = 'cb55d4f05cd443ce82303222f77ef5e0';
4 | const sentry_app = '61499';
5 | export const sentry_url = `https://${sentry_key}@app.getsentry.com/${sentry_app}`;
6 |
7 | export function logException(ex, context) {
8 | Raven.captureException(ex, {
9 | extra: context
10 | });
11 | /*eslint no-console:0*/
12 | window && window.console && console.error && console.error(ex);
13 | }
14 |
--------------------------------------------------------------------------------
/15/reducers/comments.js:
--------------------------------------------------------------------------------
1 | function comments(state = [], action) {
2 | return state;
3 | }
4 |
5 | export default comments;
6 |
--------------------------------------------------------------------------------
/15/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer } from 'react-router-redux';
3 |
4 | import posts from './posts';
5 | import comments from './comments';
6 |
7 | const rootReducer = combineReducers({posts, comments, routing: routerReducer });
8 |
9 | export default rootReducer;
10 |
--------------------------------------------------------------------------------
/15/reducers/posts.js:
--------------------------------------------------------------------------------
1 | // a reducer takes in two things:
2 |
3 | // 1. the action (info about what happened)
4 | // 2. copy of current state
5 |
6 | function posts(state = [], action) {
7 | switch(action.type) {
8 | case 'INCREMENT_LIKES' :
9 | console.log("Incrementing Likes!!");
10 | const i = action.index;
11 | return [
12 | ...state.slice(0,i), // before the one we are updating
13 | {...state[i], likes: state[i].likes + 1},
14 | ...state.slice(i + 1), // after the one we are updating
15 | ]
16 | default:
17 | return state;
18 | }
19 | }
20 |
21 | export default posts;
22 |
--------------------------------------------------------------------------------
/15/reduxstagram.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { render } from 'react-dom';
4 |
5 | // Import css
6 | import css from './styles/style.styl';
7 |
8 | // Import Components
9 | import App from './components/App';
10 | import Single from './components/Single';
11 | import PhotoGrid from './components/PhotoGrid';
12 |
13 | // import react router deps
14 | import { Router, Route, IndexRoute, browserHistory } from 'react-router';
15 | import { Provider } from 'react-redux';
16 | import store, { history } from './store';
17 |
18 | import Raven from 'raven-js';
19 | import { sentry_url, logException } from './data/config';
20 |
21 | Raven.config(sentry_url, {
22 | tags: {
23 | git_commit: 'as09d8f09'
24 | }
25 | }).install();
26 |
27 | logException(new Error('Incomplete Data!'), {
28 | email: 'wesbos@gmail.com'
29 | });
30 |
31 | Raven.showReportDialog();
32 |
33 | const router = (
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | )
43 |
44 | render(router, document.getElementById('root'));
45 |
--------------------------------------------------------------------------------
/15/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compse } from 'redux';
2 | import { syncHistoryWithStore} from 'react-router-redux';
3 | import { browserHistory } from 'react-router';
4 |
5 | // import the root reducer
6 | import rootReducer from './reducers/index';
7 |
8 | import comments from './data/comments';
9 | import posts from './data/posts';
10 |
11 | // create an object for the default data
12 | const defaultState = {
13 | posts,
14 | comments
15 | };
16 |
17 | const store = createStore(rootReducer, defaultState);
18 |
19 | export const history = syncHistoryWithStore(browserHistory, store);
20 |
21 | export default store;
22 |
--------------------------------------------------------------------------------
/15/styles/_animations.styl:
--------------------------------------------------------------------------------
1 | // offset variable gets tacked for centering in addition to the scaling
2 |
3 | offsets = translateX(-50%) translateY(-50%)
4 | .likes-heart
5 | opacity 0
6 | transition all 0.5s // time to fade out after its done
7 | transform offsets scale(5) // this is the "end state"
8 | display block
9 | &.like-enter
10 | transition all .2s
11 | transform offsets scale(1)
12 | opacity 1
13 | &.like-enter-active
14 | transform offsets scale(5)
15 | .like-leave-active
16 | display none
17 |
18 |
--------------------------------------------------------------------------------
/15/styles/_normalize.styl:
--------------------------------------------------------------------------------
1 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button],/* 1 */
2 | input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
3 |
4 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
5 |
6 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
7 |
8 |
9 | img
10 | max-width 100%
11 |
--------------------------------------------------------------------------------
/15/styles/_typography.styl:
--------------------------------------------------------------------------------
1 | /*
2 | Variables
3 | */
4 |
5 | blue = #125688
6 | offwhite = #fafafa
7 | lightgrey = #EDEEED
8 | lightgray = lightgrey // OH Canada!
9 |
10 | html
11 | font-size 10px
12 | font-family sans-serif
13 |
14 | p
15 | font-size 1.6rem
16 | line-height 1.5
17 |
18 | h1
19 | font-family billabong, 'billabongregular'
20 | text-align center
21 | font-weight 100
22 | font-size 13rem
23 | margin 2rem 0
24 | letter-spacing -1px
25 | text-shadow 0px 4px 0 rgba(18, 86, 136, 0.11)
26 | a
27 | color blue
28 | text-decoration none
29 | &:focus
30 | outline 0
31 |
32 |
33 | // "Instagram-like" webfont
34 |
35 | @font-face {
36 | font-family: 'billabongregular';
37 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot');
38 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot?#iefix') format('embedded-opentype'),
39 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.woff') format('woff'),
40 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.ttf') format('truetype'),
41 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.svg#billabongregular') format('svg');
42 | font-weight: normal;
43 | font-style: normal;
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/15/styles/style.styl:
--------------------------------------------------------------------------------
1 | @import '_normalize.styl'
2 | @import '_typography.styl'
3 | @import '_animations.styl'
4 |
5 | body
6 | background offwhite
7 |
8 | .photo-grid
9 | display flex
10 | flex-wrap wrap
11 | max-width 1200px
12 | margin 0 auto
13 |
14 | .grid-figure
15 | flex-basis calc(33.333% - 4rem)
16 | flex-grow 1
17 | flex-shrink 0
18 | margin 0 2rem 2rem 2rem
19 | padding 2rem
20 | border 1px solid lightgray
21 | background white
22 | box-shadow 0 0 0 5px rgba(0,0,0,0.03);
23 | position relative
24 |
25 | .single-photo
26 | @extend .grid-figure
27 | max-width 900px
28 | margin 0 auto
29 | display flex
30 | background white
31 | .grid-figure
32 | box-shadow none
33 | margin 0 2rem 0 0
34 | border 0
35 | padding 0
36 | flex 1 0 60%
37 | max-width 60%
38 | .comments
39 | flex 1 0 40%
40 | max-width 40%
41 | .grid-photo
42 | width 100%
43 | margin 0
44 |
45 | .grid-photo
46 | width calc(100% + 4rem)
47 | margin-left -2rem
48 | margin-top -2rem
49 | max-width none
50 |
51 | // Comments
52 | .remove-comment
53 | background none
54 | border 0
55 | line-height 1
56 | opacity 0
57 | &:hover
58 | color red
59 |
60 | .comment
61 | border-bottom 1px solid lightgrey
62 | padding 0.5rem 0
63 | p
64 | font-size 1.2rem
65 | margin 0
66 | strong
67 | color blue
68 | margin-right 5px
69 | &:hover
70 | .remove-comment
71 | opacity 1
72 |
73 | .comment-form
74 | input, textarea
75 | width 100%
76 | border 0
77 | font-size 1.3rem
78 | padding 1rem 0
79 | border-bottom 1px solid lightgrey
80 | outline none
81 | resize vertical
82 |
83 |
84 | .grid-photo-wrap
85 | position relative
86 |
87 | .likes-heart
88 | background url(http://f.cl.ly/items/3Y373q2Q3J3Y1j203n0m/Bitmap-3.png) center no-repeat
89 | background-size contain
90 | font-size 2rem
91 | padding 1rem
92 | position absolute
93 | color blue
94 | left 50%
95 | top 50%
96 | pointer-events none
97 |
98 |
99 | /*
100 | Buttons
101 | */
102 |
103 | .control-buttons
104 | display flex
105 | justify-content space-between
106 |
107 | button, .button
108 | border 2px solid lighten(grey, 90%)
109 | background none
110 | flex-basis 48%
111 | display inline-block
112 | line-height 2
113 | text-decoration none
114 | padding 5px
115 | text-align center
116 | font-size 15px
117 | color blue
118 | transition all 0.2s
119 | box-sizing padding-box
120 | &:hover, &:focus
121 | border-color blue
122 | outline 0
123 |
124 | /*
125 | Cowboy style speech bubble - you should probably use an SVG for this if you are doing more icons.
126 | */
127 | .speech-bubble
128 | size = 1.25rem
129 | width size * 1.2
130 | height size
131 | background blue
132 | display inline-block
133 | border-radius 50%
134 | position relative
135 | &:after
136 | display inline-block
137 | position absolute
138 | content ''
139 | width: 0;
140 | height: 0;
141 | border-style: solid;
142 | border-width: 0 size size 0
143 | border-color: transparent blue transparent transparent
144 | top 30%
145 | left 0
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/16/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/16/.DS_Store
--------------------------------------------------------------------------------
/16/actions/actionCreators.js:
--------------------------------------------------------------------------------
1 | // increment
2 | export function increment(index) {
3 | return {
4 | type: 'INCREMENT_LIKES',
5 | index
6 | }
7 | }
8 |
9 | // add comment
10 | export function addComment(postId, author, comment) {
11 | return {
12 | type: 'ADD_COMMENT',
13 | postId,
14 | author,
15 | comment
16 | }
17 | }
18 |
19 | // remove comment
20 |
21 | export function removeComment(postId, i) {
22 | return {
23 | type: 'REMOVE_COMMENT',
24 | i,
25 | postId
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/16/components/App.js:
--------------------------------------------------------------------------------
1 | import { bindActionCreators } from 'redux';
2 | import { connect } from 'react-redux';
3 | import * as actionCreators from '../actions/actionCreators';
4 | import Main from './Main';
5 |
6 | function mapStateToProps(state) {
7 | return {
8 | posts: state.posts,
9 | comments: state.comments
10 | }
11 | }
12 |
13 | function mapDispachToProps(dispatch) {
14 | return bindActionCreators(actionCreators, dispatch);
15 | }
16 |
17 | const App = connect(mapStateToProps, mapDispachToProps)(Main);
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/16/components/Comments.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Comments = React.createClass({
4 | renderComment(comment, i) {
5 | return (
6 |
7 |
8 | {comment.user}
9 | {comment.text}
10 |
11 |
12 |
13 | )
14 | },
15 | handleSubmit(e) {
16 | e.preventDefault();
17 | const { postId } = this.props.params;
18 | const author = this.refs.author.value;
19 | const comment = this.refs.comment.value;
20 | this.props.addComment(postId, author, comment);
21 | this.refs.commentForm.reset();
22 | },
23 | render() {
24 | return (
25 |
26 | {this.props.postComments.map(this.renderComment)}
27 |
32 |
33 | )
34 | }
35 | });
36 |
37 | export default Comments;
38 |
--------------------------------------------------------------------------------
/16/components/Main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | const Main = React.createClass({
5 | render() {
6 | return (
7 |
8 |
9 | Reduxstagram
10 |
11 | {React.cloneElement(this.props.children, this.props)}
12 |
13 | )
14 | }
15 | });
16 |
17 | export default Main;
18 |
--------------------------------------------------------------------------------
/16/components/Photo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 | import CSSTransitionGroup from 'react-addons-css-transition-group';
4 |
5 | const Photo = React.createClass({
6 | render() {
7 | const { post, i, comments } = this.props;
8 | return (
9 |
10 |
11 |
12 |

13 |
14 |
15 |
16 | {post.likes}
17 |
18 |
19 |
20 |
21 |
22 | {post.caption}
23 |
24 |
25 |
26 |
27 |
28 | {comments[post.code] ? comments[post.code].length : 0 }
29 |
30 |
31 |
32 |
33 |
34 |
35 | )
36 | }
37 | });
38 |
39 | export default Photo;
40 |
--------------------------------------------------------------------------------
/16/components/PhotoGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Photo from './Photo';
3 |
4 | const PhotoGrid = React.createClass({
5 | render() {
6 | return (
7 |
8 | {this.props.posts.map((post, i) =>
)}
9 |
10 | )
11 | }
12 | });
13 |
14 | export default PhotoGrid;
15 |
--------------------------------------------------------------------------------
/16/components/Single.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Photo from './Photo';
3 | import Comments from './Comments';
4 |
5 | const Single = React.createClass({
6 | render() {
7 | const { postId } = this.props.params;
8 |
9 | const i = this.props.posts.findIndex((post) => post.code === postId);
10 | const post = this.props.posts[i];
11 |
12 | const postComments = this.props.comments[postId] || [];
13 |
14 | return (
15 |
19 | )
20 | }
21 | });
22 |
23 | export default Single;
24 |
--------------------------------------------------------------------------------
/16/data/config.js:
--------------------------------------------------------------------------------
1 | import Raven from 'raven-js';
2 |
3 | const sentry_key = 'cb55d4f05cd443ce82303222f77ef5e0';
4 | const sentry_app = '61499';
5 | export const sentry_url = `https://${sentry_key}@app.getsentry.com/${sentry_app}`;
6 |
7 | export function logException(ex, context) {
8 | Raven.captureException(ex, {
9 | extra: context
10 | });
11 | /*eslint no-console:0*/
12 | window && window.console && console.error && console.error(ex);
13 | }
14 |
--------------------------------------------------------------------------------
/16/reducers/comments.js:
--------------------------------------------------------------------------------
1 | function comments(state = [], action) {
2 | return state;
3 | }
4 |
5 | export default comments;
6 |
--------------------------------------------------------------------------------
/16/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer } from 'react-router-redux';
3 |
4 | import posts from './posts';
5 | import comments from './comments';
6 |
7 | const rootReducer = combineReducers({posts, comments, routing: routerReducer });
8 |
9 | export default rootReducer;
10 |
--------------------------------------------------------------------------------
/16/reducers/posts.js:
--------------------------------------------------------------------------------
1 | // a reducer takes in two things:
2 |
3 | // 1. the action (info about what happened)
4 | // 2. copy of current state
5 |
6 | function posts(state = [], action) {
7 | switch(action.type) {
8 | case 'INCREMENT_LIKES' :
9 | console.log("Incrementing Likes!!");
10 | const i = action.index;
11 | return [
12 | ...state.slice(0,i), // before the one we are updating
13 | {...state[i], likes: state[i].likes + 1},
14 | ...state.slice(i + 1), // after the one we are updating
15 | ]
16 | default:
17 | return state;
18 | }
19 | }
20 |
21 | export default posts;
22 |
--------------------------------------------------------------------------------
/16/reduxstagram.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { render } from 'react-dom';
4 |
5 | // Import css
6 | import css from './styles/style.styl';
7 |
8 | // Import Components
9 | import App from './components/App';
10 | import Single from './components/Single';
11 | import PhotoGrid from './components/PhotoGrid';
12 |
13 | // import react router deps
14 | import { Router, Route, IndexRoute, browserHistory } from 'react-router';
15 | import { Provider } from 'react-redux';
16 | import store, { history } from './store';
17 |
18 | import Raven from 'raven-js';
19 | import { sentry_url, logException } from './data/config';
20 |
21 | Raven.config(sentry_url, {
22 | tags: {
23 | git_commit: 'as09d8f09'
24 | }
25 | }).install();
26 |
27 | logException(new Error('Incomplete Data!'), {
28 | email: 'wesbos@gmail.com'
29 | });
30 |
31 | Raven.showReportDialog();
32 |
33 | const router = (
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | )
43 |
44 | render(router, document.getElementById('root'));
45 |
--------------------------------------------------------------------------------
/16/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compse } from 'redux';
2 | import { syncHistoryWithStore} from 'react-router-redux';
3 | import { browserHistory } from 'react-router';
4 |
5 | // import the root reducer
6 | import rootReducer from './reducers/index';
7 |
8 | import comments from './data/comments';
9 | import posts from './data/posts';
10 |
11 | // create an object for the default data
12 | const defaultState = {
13 | posts,
14 | comments
15 | };
16 |
17 | const store = createStore(rootReducer, defaultState);
18 |
19 | export const history = syncHistoryWithStore(browserHistory, store);
20 |
21 | export default store;
22 |
--------------------------------------------------------------------------------
/16/styles/_animations.styl:
--------------------------------------------------------------------------------
1 | // offset variable gets tacked for centering in addition to the scaling
2 |
3 | offsets = translateX(-50%) translateY(-50%)
4 | .likes-heart
5 | opacity 0
6 | transition all 0.5s // time to fade out after its done
7 | transform offsets scale(5) // this is the "end state"
8 | display block
9 | &.like-enter
10 | transition all .2s
11 | transform offsets scale(1)
12 | opacity 1
13 | &.like-enter-active
14 | transform offsets scale(5)
15 | .like-leave-active
16 | display none
17 |
18 |
--------------------------------------------------------------------------------
/16/styles/_normalize.styl:
--------------------------------------------------------------------------------
1 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button],/* 1 */
2 | input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
3 |
4 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
5 |
6 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
7 |
8 |
9 | img
10 | max-width 100%
11 |
--------------------------------------------------------------------------------
/16/styles/_typography.styl:
--------------------------------------------------------------------------------
1 | /*
2 | Variables
3 | */
4 |
5 | blue = #125688
6 | offwhite = #fafafa
7 | lightgrey = #EDEEED
8 | lightgray = lightgrey // OH Canada!
9 |
10 | html
11 | font-size 10px
12 | font-family sans-serif
13 |
14 | p
15 | font-size 1.6rem
16 | line-height 1.5
17 |
18 | h1
19 | font-family billabong, 'billabongregular'
20 | text-align center
21 | font-weight 100
22 | font-size 13rem
23 | margin 2rem 0
24 | letter-spacing -1px
25 | text-shadow 0px 4px 0 rgba(18, 86, 136, 0.11)
26 | a
27 | color blue
28 | text-decoration none
29 | &:focus
30 | outline 0
31 |
32 |
33 | // "Instagram-like" webfont
34 |
35 | @font-face {
36 | font-family: 'billabongregular';
37 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot');
38 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot?#iefix') format('embedded-opentype'),
39 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.woff') format('woff'),
40 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.ttf') format('truetype'),
41 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.svg#billabongregular') format('svg');
42 | font-weight: normal;
43 | font-style: normal;
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/16/styles/style.styl:
--------------------------------------------------------------------------------
1 | @import '_normalize.styl'
2 | @import '_typography.styl'
3 | @import '_animations.styl'
4 |
5 | body
6 | background offwhite
7 |
8 | .photo-grid
9 | display flex
10 | flex-wrap wrap
11 | max-width 1200px
12 | margin 0 auto
13 |
14 | .grid-figure
15 | flex-basis calc(33.333% - 4rem)
16 | flex-grow 1
17 | flex-shrink 0
18 | margin 0 2rem 2rem 2rem
19 | padding 2rem
20 | border 1px solid lightgray
21 | background white
22 | box-shadow 0 0 0 5px rgba(0,0,0,0.03);
23 | position relative
24 |
25 | .single-photo
26 | @extend .grid-figure
27 | max-width 900px
28 | margin 0 auto
29 | display flex
30 | background white
31 | .grid-figure
32 | box-shadow none
33 | margin 0 2rem 0 0
34 | border 0
35 | padding 0
36 | flex 1 0 60%
37 | max-width 60%
38 | .comments
39 | flex 1 0 40%
40 | max-width 40%
41 | .grid-photo
42 | width 100%
43 | margin 0
44 |
45 | .grid-photo
46 | width calc(100% + 4rem)
47 | margin-left -2rem
48 | margin-top -2rem
49 | max-width none
50 |
51 | // Comments
52 | .remove-comment
53 | background none
54 | border 0
55 | line-height 1
56 | opacity 0
57 | &:hover
58 | color red
59 |
60 | .comment
61 | border-bottom 1px solid lightgrey
62 | padding 0.5rem 0
63 | p
64 | font-size 1.2rem
65 | margin 0
66 | strong
67 | color blue
68 | margin-right 5px
69 | &:hover
70 | .remove-comment
71 | opacity 1
72 |
73 | .comment-form
74 | input, textarea
75 | width 100%
76 | border 0
77 | font-size 1.3rem
78 | padding 1rem 0
79 | border-bottom 1px solid lightgrey
80 | outline none
81 | resize vertical
82 |
83 |
84 | .grid-photo-wrap
85 | position relative
86 |
87 | .likes-heart
88 | background url(http://f.cl.ly/items/3Y373q2Q3J3Y1j203n0m/Bitmap-3.png) center no-repeat
89 | background-size contain
90 | font-size 2rem
91 | padding 1rem
92 | position absolute
93 | color blue
94 | left 50%
95 | top 50%
96 | pointer-events none
97 |
98 |
99 | /*
100 | Buttons
101 | */
102 |
103 | .control-buttons
104 | display flex
105 | justify-content space-between
106 |
107 | button, .button
108 | border 2px solid lighten(grey, 90%)
109 | background none
110 | flex-basis 48%
111 | display inline-block
112 | line-height 2
113 | text-decoration none
114 | padding 5px
115 | text-align center
116 | font-size 15px
117 | color blue
118 | transition all 0.2s
119 | box-sizing padding-box
120 | &:hover, &:focus
121 | border-color blue
122 | outline 0
123 |
124 | /*
125 | Cowboy style speech bubble - you should probably use an SVG for this if you are doing more icons.
126 | */
127 | .speech-bubble
128 | size = 1.25rem
129 | width size * 1.2
130 | height size
131 | background blue
132 | display inline-block
133 | border-radius 50%
134 | position relative
135 | &:after
136 | display inline-block
137 | position absolute
138 | content ''
139 | width: 0;
140 | height: 0;
141 | border-style: solid;
142 | border-width: 0 size size 0
143 | border-color: transparent blue transparent transparent
144 | top 30%
145 | left 0
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/17/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/17/.DS_Store
--------------------------------------------------------------------------------
/17/actions/actionCreators.js:
--------------------------------------------------------------------------------
1 | // increment
2 | export function increment(index) {
3 | return {
4 | type: 'INCREMENT_LIKES',
5 | index
6 | }
7 | }
8 |
9 | // add comment
10 | export function addComment(postId, author, comment) {
11 | return {
12 | type: 'ADD_COMMENT',
13 | postId,
14 | author,
15 | comment
16 | }
17 | }
18 |
19 | // remove comment
20 |
21 | export function removeComment(postId, i) {
22 | return {
23 | type: 'REMOVE_COMMENT',
24 | i,
25 | postId
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/17/components/App.js:
--------------------------------------------------------------------------------
1 | import { bindActionCreators } from 'redux';
2 | import { connect } from 'react-redux';
3 | import * as actionCreators from '../actions/actionCreators';
4 | import Main from './Main';
5 |
6 | function mapStateToProps(state) {
7 | return {
8 | posts: state.posts,
9 | comments: state.comments
10 | }
11 | }
12 |
13 | function mapDispachToProps(dispatch) {
14 | return bindActionCreators(actionCreators, dispatch);
15 | }
16 |
17 | const App = connect(mapStateToProps, mapDispachToProps)(Main);
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/17/components/Comments.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Comments = React.createClass({
4 | renderComment(comment, i) {
5 | return (
6 |
7 |
8 | {comment.user}
9 | {comment.text}
10 |
11 |
12 |
13 | )
14 | },
15 | handleSubmit(e) {
16 | e.preventDefault();
17 | const { postId } = this.props.params;
18 | const author = this.refs.author.value;
19 | const comment = this.refs.comment.value;
20 | this.props.addComment(postId, author, comment);
21 | this.refs.commentForm.reset();
22 | },
23 | render() {
24 | return (
25 |
26 | {this.props.postComments.map(this.renderComment)}
27 |
32 |
33 | )
34 | }
35 | });
36 |
37 | export default Comments;
38 |
--------------------------------------------------------------------------------
/17/components/Main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | const Main = React.createClass({
5 | render() {
6 | return (
7 |
8 |
9 | Reduxstagram
10 |
11 | {React.cloneElement(this.props.children, this.props)}
12 |
13 | )
14 | }
15 | });
16 |
17 | export default Main;
18 |
--------------------------------------------------------------------------------
/17/components/Photo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 | import CSSTransitionGroup from 'react-addons-css-transition-group';
4 |
5 | const Photo = React.createClass({
6 | render() {
7 | const { post, i, comments } = this.props;
8 | return (
9 |
10 |
11 |
12 |

13 |
14 |
15 |
16 | {post.likes}
17 |
18 |
19 |
20 |
21 |
22 | {post.caption}
23 |
24 |
25 |
26 |
27 |
28 | {comments[post.code] ? comments[post.code].length : 0 }
29 |
30 |
31 |
32 |
33 |
34 |
35 | )
36 | }
37 | });
38 |
39 | export default Photo;
40 |
--------------------------------------------------------------------------------
/17/components/PhotoGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Photo from './Photo';
3 |
4 | const PhotoGrid = React.createClass({
5 | render() {
6 | return (
7 |
8 | {this.props.posts.map((post, i) =>
)}
9 |
10 | )
11 | }
12 | });
13 |
14 | export default PhotoGrid;
15 |
--------------------------------------------------------------------------------
/17/components/Single.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Photo from './Photo';
3 | import Comments from './Comments';
4 |
5 | const Single = React.createClass({
6 | render() {
7 | const { postId } = this.props.params;
8 |
9 | const i = this.props.posts.findIndex((post) => post.code === postId);
10 | const post = this.props.posts[i];
11 |
12 | const postComments = this.props.comments[postId] || [];
13 |
14 | return (
15 |
19 | )
20 | }
21 | });
22 |
23 | export default Single;
24 |
--------------------------------------------------------------------------------
/17/data/config.js:
--------------------------------------------------------------------------------
1 | import Raven from 'raven-js';
2 |
3 | const sentry_key = 'cb55d4f05cd443ce82303222f77ef5e0';
4 | const sentry_app = '61499';
5 | export const sentry_url = `https://${sentry_key}@app.getsentry.com/${sentry_app}`;
6 |
7 | export function logException(ex, context) {
8 | Raven.captureException(ex, {
9 | extra: context
10 | });
11 | /*eslint no-console:0*/
12 | window && window.console && console.error && console.error(ex);
13 | }
14 |
--------------------------------------------------------------------------------
/17/reducers/comments.js:
--------------------------------------------------------------------------------
1 | function postComments(state = [], action) {
2 | switch(action.type){
3 | case 'ADD_COMMENT':
4 | // return the new state with the new comment
5 | return [...state,{
6 | user: action.author,
7 | text: action.comment
8 | }];
9 | case 'REMOVE_COMMENT':
10 | // we need to return the new state without the deleted comment
11 | return [
12 | // from the start to the one we want to delete
13 | ...state.slice(0,action.i),
14 | // after the deleted one, to the end
15 | ...state.slice(action.i + 1)
16 | ]
17 | default:
18 | return state;
19 | }
20 | return state;
21 | }
22 |
23 | function comments(state = [], action) {
24 | if(typeof action.postId !== 'undefined') {
25 | return {
26 | // take the current state
27 | ...state,
28 | // overwrite this post with a new one
29 | [action.postId]: postComments(state[action.postId], action)
30 | }
31 | }
32 | return state;
33 | }
34 |
35 | export default comments;
36 |
--------------------------------------------------------------------------------
/17/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer } from 'react-router-redux';
3 |
4 | import posts from './posts';
5 | import comments from './comments';
6 |
7 | const rootReducer = combineReducers({posts, comments, routing: routerReducer });
8 |
9 | export default rootReducer;
10 |
--------------------------------------------------------------------------------
/17/reducers/posts.js:
--------------------------------------------------------------------------------
1 | // a reducer takes in two things:
2 |
3 | // 1. the action (info about what happened)
4 | // 2. copy of current state
5 |
6 | function posts(state = [], action) {
7 | switch(action.type) {
8 | case 'INCREMENT_LIKES' :
9 | console.log("Incrementing Likes!!");
10 | const i = action.index;
11 | return [
12 | ...state.slice(0,i), // before the one we are updating
13 | {...state[i], likes: state[i].likes + 1},
14 | ...state.slice(i + 1), // after the one we are updating
15 | ]
16 | default:
17 | return state;
18 | }
19 | }
20 |
21 | export default posts;
22 |
--------------------------------------------------------------------------------
/17/reduxstagram.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { render } from 'react-dom';
4 |
5 | // Import css
6 | import css from './styles/style.styl';
7 |
8 | // Import Components
9 | import App from './components/App';
10 | import Single from './components/Single';
11 | import PhotoGrid from './components/PhotoGrid';
12 |
13 | // import react router deps
14 | import { Router, Route, IndexRoute, browserHistory } from 'react-router';
15 | import { Provider } from 'react-redux';
16 | import store, { history } from './store';
17 |
18 | import Raven from 'raven-js';
19 | import { sentry_url, logException } from './data/config';
20 |
21 | Raven.config(sentry_url, {
22 | tags: {
23 | git_commit: 'as09d8f09'
24 | }
25 | }).install();
26 |
27 | logException(new Error('Incomplete Data!'), {
28 | email: 'wesbos@gmail.com'
29 | });
30 |
31 | Raven.showReportDialog();
32 |
33 | const router = (
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | )
43 |
44 | render(router, document.getElementById('root'));
45 |
--------------------------------------------------------------------------------
/17/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compse } from 'redux';
2 | import { syncHistoryWithStore} from 'react-router-redux';
3 | import { browserHistory } from 'react-router';
4 |
5 | // import the root reducer
6 | import rootReducer from './reducers/index';
7 |
8 | import comments from './data/comments';
9 | import posts from './data/posts';
10 |
11 | // create an object for the default data
12 | const defaultState = {
13 | posts,
14 | comments
15 | };
16 |
17 | const store = createStore(rootReducer, defaultState);
18 |
19 | export const history = syncHistoryWithStore(browserHistory, store);
20 |
21 | export default store;
22 |
--------------------------------------------------------------------------------
/17/styles/_animations.styl:
--------------------------------------------------------------------------------
1 | // offset variable gets tacked for centering in addition to the scaling
2 |
3 | offsets = translateX(-50%) translateY(-50%)
4 | .likes-heart
5 | opacity 0
6 | transition all 0.5s // time to fade out after its done
7 | transform offsets scale(5) // this is the "end state"
8 | display block
9 | &.like-enter
10 | transition all .2s
11 | transform offsets scale(1)
12 | opacity 1
13 | &.like-enter-active
14 | transform offsets scale(5)
15 | .like-leave-active
16 | display none
17 |
18 |
--------------------------------------------------------------------------------
/17/styles/_normalize.styl:
--------------------------------------------------------------------------------
1 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button],/* 1 */
2 | input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
3 |
4 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
5 |
6 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
7 |
8 |
9 | img
10 | max-width 100%
11 |
--------------------------------------------------------------------------------
/17/styles/_typography.styl:
--------------------------------------------------------------------------------
1 | /*
2 | Variables
3 | */
4 |
5 | blue = #125688
6 | offwhite = #fafafa
7 | lightgrey = #EDEEED
8 | lightgray = lightgrey // OH Canada!
9 |
10 | html
11 | font-size 10px
12 | font-family sans-serif
13 |
14 | p
15 | font-size 1.6rem
16 | line-height 1.5
17 |
18 | h1
19 | font-family billabong, 'billabongregular'
20 | text-align center
21 | font-weight 100
22 | font-size 13rem
23 | margin 2rem 0
24 | letter-spacing -1px
25 | text-shadow 0px 4px 0 rgba(18, 86, 136, 0.11)
26 | a
27 | color blue
28 | text-decoration none
29 | &:focus
30 | outline 0
31 |
32 |
33 | // "Instagram-like" webfont
34 |
35 | @font-face {
36 | font-family: 'billabongregular';
37 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot');
38 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot?#iefix') format('embedded-opentype'),
39 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.woff') format('woff'),
40 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.ttf') format('truetype'),
41 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.svg#billabongregular') format('svg');
42 | font-weight: normal;
43 | font-style: normal;
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/17/styles/style.styl:
--------------------------------------------------------------------------------
1 | @import '_normalize.styl'
2 | @import '_typography.styl'
3 | @import '_animations.styl'
4 |
5 | body
6 | background offwhite
7 |
8 | .photo-grid
9 | display flex
10 | flex-wrap wrap
11 | max-width 1200px
12 | margin 0 auto
13 |
14 | .grid-figure
15 | flex-basis calc(33.333% - 4rem)
16 | flex-grow 1
17 | flex-shrink 0
18 | margin 0 2rem 2rem 2rem
19 | padding 2rem
20 | border 1px solid lightgray
21 | background white
22 | box-shadow 0 0 0 5px rgba(0,0,0,0.03);
23 | position relative
24 |
25 | .single-photo
26 | @extend .grid-figure
27 | max-width 900px
28 | margin 0 auto
29 | display flex
30 | background white
31 | .grid-figure
32 | box-shadow none
33 | margin 0 2rem 0 0
34 | border 0
35 | padding 0
36 | flex 1 0 60%
37 | max-width 60%
38 | .comments
39 | flex 1 0 40%
40 | max-width 40%
41 | .grid-photo
42 | width 100%
43 | margin 0
44 |
45 | .grid-photo
46 | width calc(100% + 4rem)
47 | margin-left -2rem
48 | margin-top -2rem
49 | max-width none
50 |
51 | // Comments
52 | .remove-comment
53 | background none
54 | border 0
55 | line-height 1
56 | opacity 0
57 | &:hover
58 | color red
59 |
60 | .comment
61 | border-bottom 1px solid lightgrey
62 | padding 0.5rem 0
63 | p
64 | font-size 1.2rem
65 | margin 0
66 | strong
67 | color blue
68 | margin-right 5px
69 | &:hover
70 | .remove-comment
71 | opacity 1
72 |
73 | .comment-form
74 | input, textarea
75 | width 100%
76 | border 0
77 | font-size 1.3rem
78 | padding 1rem 0
79 | border-bottom 1px solid lightgrey
80 | outline none
81 | resize vertical
82 |
83 |
84 | .grid-photo-wrap
85 | position relative
86 |
87 | .likes-heart
88 | background url(http://f.cl.ly/items/3Y373q2Q3J3Y1j203n0m/Bitmap-3.png) center no-repeat
89 | background-size contain
90 | font-size 2rem
91 | padding 1rem
92 | position absolute
93 | color blue
94 | left 50%
95 | top 50%
96 | pointer-events none
97 |
98 |
99 | /*
100 | Buttons
101 | */
102 |
103 | .control-buttons
104 | display flex
105 | justify-content space-between
106 |
107 | button, .button
108 | border 2px solid lighten(grey, 90%)
109 | background none
110 | flex-basis 48%
111 | display inline-block
112 | line-height 2
113 | text-decoration none
114 | padding 5px
115 | text-align center
116 | font-size 15px
117 | color blue
118 | transition all 0.2s
119 | box-sizing padding-box
120 | &:hover, &:focus
121 | border-color blue
122 | outline 0
123 |
124 | /*
125 | Cowboy style speech bubble - you should probably use an SVG for this if you are doing more icons.
126 | */
127 | .speech-bubble
128 | size = 1.25rem
129 | width size * 1.2
130 | height size
131 | background blue
132 | display inline-block
133 | border-radius 50%
134 | position relative
135 | &:after
136 | display inline-block
137 | position absolute
138 | content ''
139 | width: 0;
140 | height: 0;
141 | border-style: solid;
142 | border-width: 0 size size 0
143 | border-color: transparent blue transparent transparent
144 | top 30%
145 | left 0
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/18/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/18/.DS_Store
--------------------------------------------------------------------------------
/18/actions/actionCreators.js:
--------------------------------------------------------------------------------
1 | // increment
2 | export function increment(index) {
3 | return {
4 | type: 'INCREMENT_LIKES',
5 | index
6 | }
7 | }
8 |
9 | // add comment
10 | export function addComment(postId, author, comment) {
11 | return {
12 | type: 'ADD_COMMENT',
13 | postId,
14 | author,
15 | comment
16 | }
17 | }
18 |
19 | // remove comment
20 |
21 | export function removeComment(postId, i) {
22 | return {
23 | type: 'REMOVE_COMMENT',
24 | i,
25 | postId
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/18/components/App.js:
--------------------------------------------------------------------------------
1 | import { bindActionCreators } from 'redux';
2 | import { connect } from 'react-redux';
3 | import * as actionCreators from '../actions/actionCreators';
4 | import Main from './Main';
5 |
6 | function mapStateToProps(state) {
7 | return {
8 | posts: state.posts,
9 | comments: state.comments
10 | }
11 | }
12 |
13 | function mapDispachToProps(dispatch) {
14 | return bindActionCreators(actionCreators, dispatch);
15 | }
16 |
17 | const App = connect(mapStateToProps, mapDispachToProps)(Main);
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/18/components/Comments.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Comments = React.createClass({
4 | renderComment(comment, i) {
5 | return (
6 |
7 |
8 | {comment.user}
9 | {comment.text}
10 |
11 |
12 |
13 | )
14 | },
15 | handleSubmit(e) {
16 | e.preventDefault();
17 | const { postId } = this.props.params;
18 | const author = this.refs.author.value;
19 | const comment = this.refs.comment.value;
20 | this.props.addComment(postId, author, comment);
21 | this.refs.commentForm.reset();
22 | },
23 | render() {
24 | return (
25 |
26 | {this.props.postComments.map(this.renderComment)}
27 |
32 |
33 | )
34 | }
35 | });
36 |
37 | export default Comments;
38 |
--------------------------------------------------------------------------------
/18/components/Main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | const Main = React.createClass({
5 | render() {
6 | return (
7 |
8 |
9 | Reduxstagram
10 |
11 | {React.cloneElement(this.props.children, this.props)}
12 |
13 | )
14 | }
15 | });
16 |
17 | export default Main;
18 |
--------------------------------------------------------------------------------
/18/components/Photo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 | import CSSTransitionGroup from 'react-addons-css-transition-group';
4 |
5 | const Photo = React.createClass({
6 | render() {
7 | const { post, i, comments } = this.props;
8 | return (
9 |
10 |
11 |
12 |

13 |
14 |
15 |
16 | {post.likes}
17 |
18 |
19 |
20 |
21 |
22 | {post.caption}
23 |
24 |
25 |
26 |
27 |
28 | {comments[post.code] ? comments[post.code].length : 0 }
29 |
30 |
31 |
32 |
33 |
34 |
35 | )
36 | }
37 | });
38 |
39 | export default Photo;
40 |
--------------------------------------------------------------------------------
/18/components/PhotoGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Photo from './Photo';
3 |
4 | const PhotoGrid = React.createClass({
5 | render() {
6 | return (
7 |
8 | {this.props.posts.map((post, i) =>
)}
9 |
10 | )
11 | }
12 | });
13 |
14 | export default PhotoGrid;
15 |
--------------------------------------------------------------------------------
/18/components/Single.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Photo from './Photo';
3 | import Comments from './Comments';
4 |
5 | const Single = React.createClass({
6 | render() {
7 | const { postId } = this.props.params;
8 |
9 | const i = this.props.posts.findIndex((post) => post.code === postId);
10 | const post = this.props.posts[i];
11 |
12 | const postComments = this.props.comments[postId] || [];
13 |
14 | return (
15 |
19 | )
20 | }
21 | });
22 |
23 | export default Single;
24 |
--------------------------------------------------------------------------------
/18/data/config.js:
--------------------------------------------------------------------------------
1 | import Raven from 'raven-js';
2 |
3 | const sentry_key = 'cb55d4f05cd443ce82303222f77ef5e0';
4 | const sentry_app = '61499';
5 | export const sentry_url = `https://${sentry_key}@app.getsentry.com/${sentry_app}`;
6 |
7 | export function logException(ex, context) {
8 | Raven.captureException(ex, {
9 | extra: context
10 | });
11 | /*eslint no-console:0*/
12 | window && window.console && console.error && console.error(ex);
13 | }
14 |
--------------------------------------------------------------------------------
/18/reducers/comments.js:
--------------------------------------------------------------------------------
1 | function postComments(state = [], action) {
2 | switch(action.type){
3 | case 'ADD_COMMENT':
4 | // return the new state with the new comment
5 | return [...state,{
6 | user: action.author,
7 | text: action.comment
8 | }];
9 | case 'REMOVE_COMMENT':
10 | // we need to return the new state without the deleted comment
11 | return [
12 | // from the start to the one we want to delete
13 | ...state.slice(0,action.i),
14 | // after the deleted one, to the end
15 | ...state.slice(action.i + 1)
16 | ]
17 | default:
18 | return state;
19 | }
20 | return state;
21 | }
22 |
23 | function comments(state = [], action) {
24 | if(typeof action.postId !== 'undefined') {
25 | return {
26 | // take the current state
27 | ...state,
28 | // overwrite this post with a new one
29 | [action.postId]: postComments(state[action.postId], action)
30 | }
31 | }
32 | return state;
33 | }
34 |
35 | export default comments;
36 |
--------------------------------------------------------------------------------
/18/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer } from 'react-router-redux';
3 |
4 | import posts from './posts';
5 | import comments from './comments';
6 |
7 | const rootReducer = combineReducers({posts, comments, routing: routerReducer });
8 |
9 | export default rootReducer;
10 |
--------------------------------------------------------------------------------
/18/reducers/posts.js:
--------------------------------------------------------------------------------
1 | function posts(state = [], action) {
2 | switch(action.type) {
3 | case 'INCREMENT_LIKES' :
4 | console.log("Incrementing Likes!!");
5 | const i = action.index;
6 | return [
7 | ...state.slice(0,i), // before the one we are updating
8 | {...state[i], likes: state[i].likes + 1},
9 | ...state.slice(i + 1), // after the one we are updating
10 | ]
11 | default:
12 | return state;
13 | }
14 | }
15 |
16 | export default posts;
17 |
--------------------------------------------------------------------------------
/18/reduxstagram.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { render } from 'react-dom';
4 |
5 | // Import css
6 | import css from './styles/style.styl';
7 |
8 | // Import Components
9 | import App from './components/App';
10 | import Single from './components/Single';
11 | import PhotoGrid from './components/PhotoGrid';
12 |
13 | // import react router deps
14 | import { Router, Route, IndexRoute, browserHistory } from 'react-router';
15 | import { Provider } from 'react-redux';
16 | import store, { history } from './store';
17 |
18 | const router = (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | )
28 |
29 | render(router, document.getElementById('root'));
30 |
--------------------------------------------------------------------------------
/18/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compose } from 'redux';
2 | import { syncHistoryWithStore} from 'react-router-redux';
3 | import { browserHistory } from 'react-router';
4 |
5 | // import the root reducer
6 | import rootReducer from './reducers/index';
7 |
8 | import comments from './data/comments';
9 | import posts from './data/posts';
10 |
11 | // create an object for the default data
12 | const defaultState = {
13 | posts,
14 | comments
15 | };
16 |
17 | const store = createStore(rootReducer, defaultState);
18 |
19 | export const history = syncHistoryWithStore(browserHistory, store);
20 |
21 | export default store;
22 |
--------------------------------------------------------------------------------
/18/styles/_animations.styl:
--------------------------------------------------------------------------------
1 | // offset variable gets tacked for centering in addition to the scaling
2 |
3 | offsets = translateX(-50%) translateY(-50%)
4 | .likes-heart
5 | opacity 0
6 | transition all 0.5s // time to fade out after its done
7 | transform offsets scale(5) // this is the "end state"
8 | display block
9 | &.like-enter
10 | transition all .2s
11 | transform offsets scale(1)
12 | opacity 1
13 | &.like-enter-active
14 | transform offsets scale(5)
15 | .like-leave-active
16 | display none
17 |
18 |
--------------------------------------------------------------------------------
/18/styles/_normalize.styl:
--------------------------------------------------------------------------------
1 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button],/* 1 */
2 | input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
3 |
4 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
5 |
6 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
7 |
8 |
9 | img
10 | max-width 100%
11 |
--------------------------------------------------------------------------------
/18/styles/_typography.styl:
--------------------------------------------------------------------------------
1 | /*
2 | Variables
3 | */
4 |
5 | blue = #125688
6 | offwhite = #fafafa
7 | lightgrey = #EDEEED
8 | lightgray = lightgrey // OH Canada!
9 |
10 | html
11 | font-size 10px
12 | font-family sans-serif
13 |
14 | p
15 | font-size 1.6rem
16 | line-height 1.5
17 |
18 | h1
19 | font-family billabong, 'billabongregular'
20 | text-align center
21 | font-weight 100
22 | font-size 13rem
23 | margin 2rem 0
24 | letter-spacing -1px
25 | text-shadow 0px 4px 0 rgba(18, 86, 136, 0.11)
26 | a
27 | color blue
28 | text-decoration none
29 | &:focus
30 | outline 0
31 |
32 |
33 | // "Instagram-like" webfont
34 |
35 | @font-face {
36 | font-family: 'billabongregular';
37 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot');
38 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot?#iefix') format('embedded-opentype'),
39 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.woff') format('woff'),
40 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.ttf') format('truetype'),
41 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.svg#billabongregular') format('svg');
42 | font-weight: normal;
43 | font-style: normal;
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/18/styles/style.styl:
--------------------------------------------------------------------------------
1 | @import '_normalize.styl'
2 | @import '_typography.styl'
3 | @import '_animations.styl'
4 |
5 | body
6 | background offwhite
7 |
8 | .photo-grid
9 | display flex
10 | flex-wrap wrap
11 | max-width 1200px
12 | margin 0 auto
13 |
14 | .grid-figure
15 | flex-basis calc(33.333% - 4rem)
16 | flex-grow 1
17 | flex-shrink 0
18 | margin 0 2rem 2rem 2rem
19 | padding 2rem
20 | border 1px solid lightgray
21 | background white
22 | box-shadow 0 0 0 5px rgba(0,0,0,0.03);
23 | position relative
24 |
25 | .single-photo
26 | @extend .grid-figure
27 | max-width 900px
28 | margin 0 auto
29 | display flex
30 | background white
31 | .grid-figure
32 | box-shadow none
33 | margin 0 2rem 0 0
34 | border 0
35 | padding 0
36 | flex 1 0 60%
37 | max-width 60%
38 | .comments
39 | flex 1 0 40%
40 | max-width 40%
41 | .grid-photo
42 | width 100%
43 | margin 0
44 |
45 | .grid-photo
46 | width calc(100% + 4rem)
47 | margin-left -2rem
48 | margin-top -2rem
49 | max-width none
50 |
51 | // Comments
52 | .remove-comment
53 | background none
54 | border 0
55 | line-height 1
56 | opacity 0
57 | &:hover
58 | color red
59 |
60 | .comment
61 | border-bottom 1px solid lightgrey
62 | padding 0.5rem 0
63 | p
64 | font-size 1.2rem
65 | margin 0
66 | strong
67 | color blue
68 | margin-right 5px
69 | &:hover
70 | .remove-comment
71 | opacity 1
72 |
73 | .comment-form
74 | input, textarea
75 | width 100%
76 | border 0
77 | font-size 1.3rem
78 | padding 1rem 0
79 | border-bottom 1px solid lightgrey
80 | outline none
81 | resize vertical
82 |
83 |
84 | .grid-photo-wrap
85 | position relative
86 |
87 | .likes-heart
88 | background url(http://f.cl.ly/items/3Y373q2Q3J3Y1j203n0m/Bitmap-3.png) center no-repeat
89 | background-size contain
90 | font-size 2rem
91 | padding 1rem
92 | position absolute
93 | color blue
94 | left 50%
95 | top 50%
96 | pointer-events none
97 |
98 |
99 | /*
100 | Buttons
101 | */
102 |
103 | .control-buttons
104 | display flex
105 | justify-content space-between
106 |
107 | button, .button
108 | border 2px solid lighten(grey, 90%)
109 | background none
110 | flex-basis 48%
111 | display inline-block
112 | line-height 2
113 | text-decoration none
114 | padding 5px
115 | text-align center
116 | font-size 15px
117 | color blue
118 | transition all 0.2s
119 | box-sizing padding-box
120 | &:hover, &:focus
121 | border-color blue
122 | outline 0
123 |
124 | /*
125 | Cowboy style speech bubble - you should probably use an SVG for this if you are doing more icons.
126 | */
127 | .speech-bubble
128 | size = 1.25rem
129 | width size * 1.2
130 | height size
131 | background blue
132 | display inline-block
133 | border-radius 50%
134 | position relative
135 | &:after
136 | display inline-block
137 | position absolute
138 | content ''
139 | width: 0;
140 | height: 0;
141 | border-style: solid;
142 | border-width: 0 size size 0
143 | border-color: transparent blue transparent transparent
144 | top 30%
145 | left 0
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/19/actions/actionCreators.js:
--------------------------------------------------------------------------------
1 | // increment
2 | export function increment(index) {
3 | return {
4 | type: 'INCREMENT_LIKES',
5 | index
6 | }
7 | }
8 |
9 | // add comment
10 | export function addComment(postId, author, comment) {
11 | return {
12 | type: 'ADD_COMMENT',
13 | postId,
14 | author,
15 | comment
16 | }
17 | }
18 |
19 | // remove comment
20 |
21 | export function removeComment(postId, i) {
22 | return {
23 | type: 'REMOVE_COMMENT',
24 | i,
25 | postId
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/19/components/App.js:
--------------------------------------------------------------------------------
1 | import { bindActionCreators } from 'redux';
2 | import { connect } from 'react-redux';
3 | import * as actionCreators from '../actions/actionCreators';
4 | import Main from './Main';
5 |
6 | function mapStateToProps(state) {
7 | return {
8 | posts: state.posts,
9 | comments: state.comments
10 | }
11 | }
12 |
13 | function mapDispachToProps(dispatch) {
14 | return bindActionCreators(actionCreators, dispatch);
15 | }
16 |
17 | const App = connect(mapStateToProps, mapDispachToProps)(Main);
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/19/components/Comments.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Comments = React.createClass({
4 | renderComment(comment, i) {
5 | return (
6 |
7 |
8 | {comment.user}
9 | {comment.text}
10 |
11 |
12 |
13 | )
14 | },
15 | handleSubmit(e) {
16 | e.preventDefault();
17 | const { postId } = this.props.params;
18 | const author = this.refs.author.value;
19 | const comment = this.refs.comment.value;
20 | this.props.addComment(postId, author, comment);
21 | this.refs.commentForm.reset();
22 | },
23 | render() {
24 | return (
25 |
26 | {this.props.postComments.map(this.renderComment)}
27 |
32 |
33 | )
34 | }
35 | });
36 |
37 | export default Comments;
38 |
--------------------------------------------------------------------------------
/19/components/Main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 |
4 | const Main = React.createClass({
5 | render() {
6 | return (
7 |
8 |
9 | Reduxstagram
10 |
11 | {React.cloneElement({...this.props}.children, {...this.props})}
12 |
13 | )
14 | }
15 | });
16 |
17 | export default Main;
18 |
--------------------------------------------------------------------------------
/19/components/Photo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router';
3 | import CSSTransitionGroup from 'react-addons-css-transition-group';
4 |
5 | const Photo = React.createClass({
6 | render() {
7 | const { post, i, comments } = this.props;
8 | return (
9 |
10 |
11 |
12 |

13 |
14 |
15 |
16 | {post.likes}
17 |
18 |
19 |
20 |
21 |
22 | {post.caption}
23 |
24 |
25 |
26 |
27 |
28 | {comments[post.code] ? comments[post.code].length : 0 }
29 |
30 |
31 |
32 |
33 |
34 |
35 | )
36 | }
37 | });
38 |
39 | export default Photo;
40 |
--------------------------------------------------------------------------------
/19/components/PhotoGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Photo from './Photo';
3 |
4 | const PhotoGrid = React.createClass({
5 | render() {
6 | return (
7 |
8 | {this.props.posts.map((post, i) =>
)}
9 |
10 | )
11 | }
12 | });
13 |
14 | export default PhotoGrid;
15 |
--------------------------------------------------------------------------------
/19/components/Single.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Photo from './Photo';
3 | import Comments from './Comments';
4 |
5 | const Single = React.createClass({
6 | render() {
7 | const { postId } = this.props.params;
8 |
9 | const i = this.props.posts.findIndex((post) => post.code === postId);
10 | const post = this.props.posts[i];
11 |
12 | const postComments = this.props.comments[postId] || [];
13 |
14 | return (
15 |
19 | )
20 | }
21 | });
22 |
23 | export default Single;
24 |
--------------------------------------------------------------------------------
/19/data/config.js:
--------------------------------------------------------------------------------
1 | import Raven from 'raven-js';
2 |
3 | const sentry_key = 'cb55d4f05cd443ce82303222f77ef5e0';
4 | const sentry_app = '61499';
5 | export const sentry_url = `https://${sentry_key}@app.getsentry.com/${sentry_app}`;
6 |
7 | export function logException(ex, context) {
8 | Raven.captureException(ex, {
9 | extra: context
10 | });
11 | /*eslint no-console:0*/
12 | window && window.console && console.error && console.error(ex);
13 | }
14 |
--------------------------------------------------------------------------------
/19/reducers/comments.js:
--------------------------------------------------------------------------------
1 | function postComments(state = [], action) {
2 | switch(action.type){
3 | case 'ADD_COMMENT':
4 | // return the new state with the new comment
5 | return [...state,{
6 | user: action.author,
7 | text: action.comment
8 | }];
9 | case 'REMOVE_COMMENT':
10 | // we need to return the new state without the deleted comment
11 | return [
12 | // from the start to the one we want to delete
13 | ...state.slice(0,action.i),
14 | // after the deleted one, to the end
15 | ...state.slice(action.i + 1)
16 | ]
17 | default:
18 | return state;
19 | }
20 | return state;
21 | }
22 |
23 | function comments(state = [], action) {
24 | if(typeof action.postId !== 'undefined') {
25 | return {
26 | // take the current state
27 | ...state,
28 | // overwrite this post with a new one
29 | [action.postId]: postComments(state[action.postId], action)
30 | }
31 | }
32 | return state;
33 | }
34 |
35 | export default comments;
36 |
--------------------------------------------------------------------------------
/19/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer } from 'react-router-redux';
3 |
4 | import posts from './posts';
5 | import comments from './comments';
6 |
7 | const rootReducer = combineReducers({posts, comments, routing: routerReducer });
8 |
9 | export default rootReducer;
10 |
--------------------------------------------------------------------------------
/19/reducers/posts.js:
--------------------------------------------------------------------------------
1 | function posts(state = [], action) {
2 | switch(action.type) {
3 | case 'INCREMENT_LIKES' :
4 | console.log("Incrementing Likes!!");
5 | const i = action.index;
6 | return [
7 | ...state.slice(0,i), // before the one we are updating
8 | {...state[i], likes: state[i].likes + 1},
9 | ...state.slice(i + 1), // after the one we are updating
10 | ]
11 | default:
12 | return state;
13 | }
14 | }
15 |
16 | export default posts;
17 |
--------------------------------------------------------------------------------
/19/reduxstagram.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { render } from 'react-dom';
4 |
5 | // Import css
6 | import css from './styles/style.styl';
7 |
8 | // Import Components
9 | import App from './components/App';
10 | import Single from './components/Single';
11 | import PhotoGrid from './components/PhotoGrid';
12 |
13 | // import react router deps
14 | import { Router, Route, IndexRoute, browserHistory } from 'react-router';
15 | import { Provider } from 'react-redux';
16 | import store, { history } from './store';
17 |
18 | const router = (
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | )
28 |
29 | render(router, document.getElementById('root'));
30 |
--------------------------------------------------------------------------------
/19/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, compose } from 'redux';
2 | import { syncHistoryWithStore} from 'react-router-redux';
3 | import { browserHistory } from 'react-router';
4 |
5 | // import the root reducer
6 | import rootReducer from './reducers/index';
7 |
8 | import comments from './data/comments';
9 | import posts from './data/posts';
10 |
11 | // create an object for the default data
12 | const defaultState = {
13 | posts,
14 | comments
15 | };
16 |
17 | const store = createStore(rootReducer, defaultState);
18 |
19 | export const history = syncHistoryWithStore(browserHistory, store);
20 |
21 | if(module.hot) {
22 | module.hot.accept('./reducers/',() => {
23 | const nextRootReducer = require('./reducers/index').default;
24 | store.replaceReducer(nextRootReducer);
25 | });
26 | }
27 |
28 | export default store;
29 |
--------------------------------------------------------------------------------
/19/styles/_animations.styl:
--------------------------------------------------------------------------------
1 | // offset variable gets tacked for centering in addition to the scaling
2 |
3 | offsets = translateX(-50%) translateY(-50%)
4 | .likes-heart
5 | opacity 0
6 | transition all 0.5s // time to fade out after its done
7 | transform offsets scale(5) // this is the "end state"
8 | display block
9 | &.like-enter
10 | transition all .2s
11 | transform offsets scale(1)
12 | opacity 1
13 | &.like-enter-active
14 | transform offsets scale(5)
15 | .like-leave-active
16 | display none
17 |
18 |
--------------------------------------------------------------------------------
/19/styles/_normalize.styl:
--------------------------------------------------------------------------------
1 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button],/* 1 */
2 | input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
3 |
4 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
5 |
6 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
7 |
8 |
9 | img
10 | max-width 100%
11 |
--------------------------------------------------------------------------------
/19/styles/_typography.styl:
--------------------------------------------------------------------------------
1 | /*
2 | Variables
3 | */
4 |
5 | blue = #125688
6 | offwhite = #fafafa
7 | lightgrey = #EDEEED
8 | lightgray = lightgrey // OH Canada!
9 |
10 | html
11 | font-size 10px
12 | font-family sans-serif
13 |
14 | p
15 | font-size 1.6rem
16 | line-height 1.5
17 |
18 | h1
19 | font-family billabong, 'billabongregular'
20 | text-align center
21 | font-weight 100
22 | font-size 13rem
23 | margin 2rem 0
24 | letter-spacing -1px
25 | text-shadow 0px 4px 0 rgba(18, 86, 136, 0.11)
26 | a
27 | color blue
28 | text-decoration none
29 | &:focus
30 | outline 0
31 |
32 |
33 | // "Instagram-like" webfont
34 |
35 | @font-face {
36 | font-family: 'billabongregular';
37 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot');
38 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot?#iefix') format('embedded-opentype'),
39 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.woff') format('woff'),
40 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.ttf') format('truetype'),
41 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.svg#billabongregular') format('svg');
42 | font-weight: normal;
43 | font-style: normal;
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/19/styles/style.styl:
--------------------------------------------------------------------------------
1 | @import '_normalize.styl'
2 | @import '_typography.styl'
3 | @import '_animations.styl'
4 |
5 | body
6 | background offwhite
7 |
8 | .photo-grid
9 | display flex
10 | flex-wrap wrap
11 | max-width 1200px
12 | margin 0 auto
13 |
14 | .grid-figure
15 | flex-basis calc(33.333% - 4rem)
16 | flex-grow 1
17 | flex-shrink 0
18 | margin 0 2rem 2rem 2rem
19 | padding 2rem
20 | border 1px solid lightgray
21 | background white
22 | box-shadow 0 0 0 5px rgba(0,0,0,0.03);
23 | position relative
24 |
25 | .single-photo
26 | @extend .grid-figure
27 | max-width 900px
28 | margin 0 auto
29 | display flex
30 | background white
31 | .grid-figure
32 | box-shadow none
33 | margin 0 2rem 0 0
34 | border 0
35 | padding 0
36 | flex 1 0 60%
37 | max-width 60%
38 | .comments
39 | flex 1 0 40%
40 | max-width 40%
41 | .grid-photo
42 | width 100%
43 | margin 0
44 |
45 | .grid-photo
46 | width calc(100% + 4rem)
47 | margin-left -2rem
48 | margin-top -2rem
49 | max-width none
50 |
51 | // Comments
52 | .remove-comment
53 | background none
54 | border 0
55 | line-height 1
56 | opacity 0
57 | &:hover
58 | color red
59 |
60 | .comment
61 | border-bottom 1px solid lightgrey
62 | padding 0.5rem 0
63 | p
64 | font-size 1.2rem
65 | margin 0
66 | strong
67 | color blue
68 | margin-right 5px
69 | &:hover
70 | .remove-comment
71 | opacity 1
72 |
73 | .comment-form
74 | input, textarea
75 | width 100%
76 | border 0
77 | font-size 1.3rem
78 | padding 1rem 0
79 | border-bottom 1px solid lightgrey
80 | outline none
81 | resize vertical
82 |
83 |
84 | .grid-photo-wrap
85 | position relative
86 |
87 | .likes-heart
88 | background url(http://f.cl.ly/items/3Y373q2Q3J3Y1j203n0m/Bitmap-3.png) center no-repeat
89 | background-size contain
90 | font-size 2rem
91 | padding 1rem
92 | position absolute
93 | color blue
94 | left 50%
95 | top 50%
96 | pointer-events none
97 |
98 |
99 | /*
100 | Buttons
101 | */
102 |
103 | .control-buttons
104 | display flex
105 | justify-content space-between
106 |
107 | button, .button
108 | border 2px solid lighten(grey, 90%)
109 | background none
110 | flex-basis 48%
111 | display inline-block
112 | line-height 2
113 | text-decoration none
114 | padding 5px
115 | text-align center
116 | font-size 15px
117 | color blue
118 | transition all 0.2s
119 | box-sizing padding-box
120 | &:hover, &:focus
121 | border-color blue
122 | outline 0
123 |
124 | /*
125 | Cowboy style speech bubble - you should probably use an SVG for this if you are doing more icons.
126 | */
127 | .speech-bubble
128 | size = 1.25rem
129 | width size * 1.2
130 | height size
131 | background blue
132 | display inline-block
133 | border-radius 50%
134 | position relative
135 | &:after
136 | display inline-block
137 | position absolute
138 | content ''
139 | width: 0;
140 | height: 0;
141 | border-style: solid;
142 | border-width: 0 size size 0
143 | border-color: transparent blue transparent transparent
144 | top 30%
145 | left 0
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/learn-redux/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/learn-redux/.DS_Store
--------------------------------------------------------------------------------
/learn-redux/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015"],
3 | "env": {
4 | "development": {
5 | "plugins": [
6 | ["transform-object-rest-spread"],
7 | ["transform-react-display-name"],
8 | ["react-transform", {
9 | "transforms": [{
10 | "transform": "react-transform-hmr",
11 | "imports": ["react"],
12 | "locals": ["module"]
13 | }, {
14 | "transform": "react-transform-catch-errors",
15 | "imports": ["react", "redbox-react"]
16 | }]
17 | }]
18 | ]
19 | },
20 | "production": {
21 | "plugins": [
22 | ["transform-object-rest-spread"],
23 | ["transform-react-display-name"]
24 | ]
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/learn-redux/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "ecmaFeatures": {
3 | "jsx": true,
4 | "modules": true
5 | },
6 | "env": {
7 | "browser": true,
8 | "node": true
9 | },
10 | "parser": "babel-eslint",
11 | "rules": {
12 | "quotes": [2, "single"],
13 | "strict": [2, "never"],
14 | "babel/generator-star-spacing": 1,
15 | "babel/new-cap": 1,
16 | "babel/object-shorthand": 1,
17 | "babel/arrow-parens": 1,
18 | "babel/no-await-in-loop": 1,
19 | "react/jsx-uses-react": 2,
20 | "react/jsx-uses-vars": 2,
21 | "react/react-in-jsx-scope": 2
22 | },
23 | "plugins": [
24 | "babel",
25 | "react"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/learn-redux/client/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wesbos/Learn-Redux-Starter-Files/38d37295ca7bb47cb20c92b74140e5b2ff6c122d/learn-redux/client/.DS_Store
--------------------------------------------------------------------------------
/learn-redux/client/data/config.js:
--------------------------------------------------------------------------------
1 | import Raven from 'raven-js';
2 |
3 | const sentry_key = 'cb55d4f05cd443ce82303222f77ef5e0';
4 | const sentry_app = '61499';
5 | export const sentry_url = `https://${sentry_key}@app.getsentry.com/${sentry_app}`;
6 |
7 | export function logException(ex, context) {
8 | Raven.captureException(ex, {
9 | extra: context
10 | });
11 | /*eslint no-console:0*/
12 | window && window.console && console.error && console.error(ex);
13 | }
14 |
--------------------------------------------------------------------------------
/learn-redux/client/reduxstagram.js:
--------------------------------------------------------------------------------
1 | // let's go!
2 |
--------------------------------------------------------------------------------
/learn-redux/client/styles/_animations.styl:
--------------------------------------------------------------------------------
1 | // offset variable gets tacked for centering in addition to the scaling
2 |
3 | offsets = translateX(-50%) translateY(-50%)
4 | .likes-heart
5 | opacity 0
6 | transition all 0.5s // time to fade out after its done
7 | transform offsets scale(5) // this is the "end state"
8 | display block
9 | &.like-enter
10 | transition all .2s
11 | transform offsets scale(1)
12 | opacity 1
13 | &.like-enter-active
14 | transform offsets scale(5)
15 | .like-leave-active
16 | display none
17 |
18 |
--------------------------------------------------------------------------------
/learn-redux/client/styles/_normalize.styl:
--------------------------------------------------------------------------------
1 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:700;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace, serif;font-size:1em;}pre{white-space:pre-wrap;word-wrap:break-word;}q{quotes:\201C \201D \2018 \2019;}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-.5em;}sub{bottom:-.25em;}img{border:0;}svg:not(:root){overflow:hidden;}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,html input[type=button],/* 1 */
2 | input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;}button[disabled],input[disabled]{cursor:default;}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0;}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}body,figure{margin:0;}legend,button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
3 |
4 | .clearfix:after {visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
5 |
6 | * { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
7 |
8 |
9 | img
10 | max-width 100%
11 |
--------------------------------------------------------------------------------
/learn-redux/client/styles/_typography.styl:
--------------------------------------------------------------------------------
1 | /*
2 | Variables
3 | */
4 |
5 | blue = #125688
6 | offwhite = #fafafa
7 | lightgrey = #EDEEED
8 | lightgray = lightgrey // OH Canada!
9 |
10 | html
11 | font-size 10px
12 | font-family sans-serif
13 |
14 | p
15 | font-size 1.6rem
16 | line-height 1.5
17 |
18 | h1
19 | font-family billabong, 'billabongregular'
20 | text-align center
21 | font-weight 100
22 | font-size 13rem
23 | margin 2rem 0
24 | letter-spacing -1px
25 | text-shadow 0px 4px 0 rgba(18, 86, 136, 0.11)
26 | a
27 | color blue
28 | text-decoration none
29 | &:focus
30 | outline 0
31 |
32 |
33 | // "Instagram-like" webfont
34 |
35 | @font-face {
36 | font-family: 'billabongregular';
37 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot');
38 | src: url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.eot?#iefix') format('embedded-opentype'),
39 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.woff') format('woff'),
40 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.ttf') format('truetype'),
41 | url('https://cdn.rawgit.com/milktronics/beaglegr.am/master/public/fonts/billabong-webfont.svg#billabongregular') format('svg');
42 | font-weight: normal;
43 | font-style: normal;
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/learn-redux/client/styles/style.styl:
--------------------------------------------------------------------------------
1 | @import '_normalize.styl'
2 | @import '_typography.styl'
3 | @import '_animations.styl'
4 |
5 | body
6 | background offwhite
7 |
8 | .photo-grid
9 | display flex
10 | flex-wrap wrap
11 | max-width 1200px
12 | margin 0 auto
13 |
14 | .grid-figure
15 | flex-basis calc(33.333% - 4rem)
16 | flex-grow 1
17 | flex-shrink 0
18 | margin 0 2rem 2rem 2rem
19 | padding 2rem
20 | border 1px solid lightgray
21 | background white
22 | box-shadow 0 0 0 5px rgba(0,0,0,0.03);
23 | position relative
24 |
25 | .single-photo
26 | @extend .grid-figure
27 | max-width 900px
28 | margin 0 auto
29 | display flex
30 | background white
31 | .grid-figure
32 | box-shadow none
33 | margin 0 2rem 0 0
34 | border 0
35 | padding 0
36 | flex 1 0 60%
37 | max-width 60%
38 | .comments
39 | flex 1 0 40%
40 | max-width 40%
41 | .grid-photo
42 | width 100%
43 | margin 0
44 |
45 | .grid-photo
46 | width calc(100% + 4rem)
47 | margin-left -2rem
48 | margin-top -2rem
49 | max-width none
50 |
51 | // Comments
52 | .remove-comment
53 | background none
54 | border 0
55 | line-height 1
56 | opacity 0
57 | &:hover
58 | color red
59 |
60 | .comment
61 | border-bottom 1px solid lightgrey
62 | padding 0.5rem 0
63 | p
64 | font-size 1.2rem
65 | margin 0
66 | strong
67 | color blue
68 | margin-right 5px
69 | &:hover
70 | .remove-comment
71 | opacity 1
72 |
73 | .comment-form
74 | input, textarea
75 | width 100%
76 | border 0
77 | font-size 1.3rem
78 | padding 1rem 0
79 | border-bottom 1px solid lightgrey
80 | outline none
81 | resize vertical
82 |
83 |
84 | .grid-photo-wrap
85 | position relative
86 |
87 | .likes-heart
88 | background url(http://f.cl.ly/items/3Y373q2Q3J3Y1j203n0m/Bitmap-3.png) center no-repeat
89 | background-size contain
90 | font-size 2rem
91 | padding 1rem
92 | position absolute
93 | color blue
94 | left 50%
95 | top 50%
96 | pointer-events none
97 |
98 |
99 | /*
100 | Buttons
101 | */
102 |
103 | .control-buttons
104 | display flex
105 | justify-content space-between
106 |
107 | button, .button
108 | border 2px solid lighten(grey, 90%)
109 | background none
110 | flex-basis 48%
111 | display inline-block
112 | line-height 2
113 | text-decoration none
114 | padding 5px
115 | text-align center
116 | font-size 15px
117 | color blue
118 | transition all 0.2s
119 | box-sizing padding-box
120 | &:hover, &:focus
121 | border-color blue
122 | outline 0
123 |
124 | /*
125 | Cowboy style speech bubble - you should probably use an SVG for this if you are doing more icons.
126 | */
127 | .speech-bubble
128 | size = 1.25rem
129 | width size * 1.2
130 | height size
131 | background blue
132 | display inline-block
133 | border-radius 50%
134 | position relative
135 | &:after
136 | display inline-block
137 | position absolute
138 | content ''
139 | width: 0;
140 | height: 0;
141 | border-style: solid;
142 | border-width: 0 size size 0
143 | border-color: transparent blue transparent transparent
144 | top 30%
145 | left 0
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/learn-redux/devServer.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var express = require('express');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config.dev');
5 |
6 | var app = express();
7 | var compiler = webpack(config);
8 |
9 | app.use(require('webpack-dev-middleware')(compiler, {
10 | noInfo: true,
11 | publicPath: config.output.publicPath
12 | }));
13 |
14 | app.use(require('webpack-hot-middleware')(compiler));
15 |
16 | app.get('*', function(req, res) {
17 | res.sendFile(path.join(__dirname, 'index.html'));
18 | });
19 |
20 | app.listen(7770, 'localhost', function(err) {
21 | if (err) {
22 | console.log(err);
23 | return;
24 | }
25 |
26 | console.log('Listening at http://localhost:7770');
27 | });
28 |
--------------------------------------------------------------------------------
/learn-redux/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Reduxstagram
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/learn-redux/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "learn-redux",
3 | "version": "1.0.0",
4 | "description": ":) ",
5 | "scripts": {
6 | "build:webpack": "NODE_ENV=production webpack --config webpack.config.prod.js",
7 | "build": "npm run clean && npm run build:webpack",
8 | "test": "NODE_ENV=production mocha './tests/**/*.spec.js' --compilers js:babel-core/register",
9 | "clean": "rimraf dist",
10 | "start": "node devServer.js",
11 | "tunnel": "browser-sync start --proxy localhost:7770 --tunnel wesbos"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/wesbos/Learn-Redux.git"
16 | },
17 | "author": "Wes Bos",
18 | "license": "MIT",
19 | "homepage": "https://github.com/wesbos/Learn-Redux",
20 | "dependencies": {
21 | "babel-core": "^6.7.7",
22 | "babel-eslint": "^6.0.4",
23 | "babel-loader": "^6.2.4",
24 | "babel-plugin-react-transform": "^2.0.2",
25 | "babel-plugin-transform-object-rest-spread": "^6.6.5",
26 | "babel-plugin-transform-react-display-name": "^6.5.0",
27 | "babel-polyfill": "^6.7.4",
28 | "babel-preset-es2015": "^6.6.0",
29 | "babel-preset-react": "^6.5.0",
30 | "css-loader": "^0.23.1",
31 | "eslint": "^2.9.0",
32 | "eslint-plugin-babel": "^3.2.0",
33 | "eslint-plugin-react": "^5.0.1",
34 | "express": "^4.13.4",
35 | "raven-js": "^2.3.0",
36 | "react": "^15.0.2",
37 | "react-addons-css-transition-group": "^15.0.2",
38 | "react-dom": "^15.0.2",
39 | "react-redux": "^4.4.5",
40 | "react-router": "^2.4.0",
41 | "react-router-redux": "^4.0.4",
42 | "react-transform-catch-errors": "^1.0.2",
43 | "react-transform-hmr": "^1.0.4",
44 | "redbox-react": "^1.2.3",
45 | "redux": "^3.5.2",
46 | "rimraf": "^2.5.2",
47 | "style-loader": "^0.13.1",
48 | "stylus": "^0.54.5",
49 | "stylus-loader": "^2.0.0",
50 | "webpack": "^1.13.0",
51 | "webpack-dev-middleware": "^1.6.1",
52 | "webpack-hot-middleware": "^2.10.0"
53 | },
54 | "devDependencies": {
55 | "expect": "^1.18.0",
56 | "expect-jsx": "^2.5.1",
57 | "mocha": "^2.4.5",
58 | "react-addons-test-utils": "^15.0.2"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/learn-redux/readme.md:
--------------------------------------------------------------------------------
1 | # Learn Redux
2 |
3 | A simple React + Redux implementation. This will be turned into a free video series once the app is totally fleshed out.
4 |
5 | ## Running
6 |
7 | First `npm install` to grab all the necessary dependencies.
8 |
9 | Then run `npm start` and open in your browser.
10 |
11 | ## Production Build
12 |
13 | Run `npm build` to create a distro folder and a bundle.js file.
14 |
--------------------------------------------------------------------------------
/learn-redux/webpack.config.dev.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 | 'webpack-hot-middleware/client',
8 | './client/reduxstagram'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/static/'
14 | },
15 | plugins: [
16 | new webpack.HotModuleReplacementPlugin(),
17 | new webpack.NoErrorsPlugin()
18 | ],
19 | module: {
20 | loaders: [
21 | // js
22 | {
23 | test: /\.js$/,
24 | loaders: ['babel'],
25 | include: path.join(__dirname, 'client')
26 | },
27 | // CSS
28 | {
29 | test: /\.styl$/,
30 | include: path.join(__dirname, 'client'),
31 | loader: 'style-loader!css-loader!stylus-loader'
32 | }
33 | ]
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/learn-redux/webpack.config.prod.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'source-map',
6 | entry: [
7 |
8 | './client/reduxstagram'
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'dist'),
12 | filename: 'bundle.js',
13 | publicPath: '/static/'
14 | },
15 | plugins: [
16 | new webpack.optimize.OccurenceOrderPlugin(),
17 | new webpack.DefinePlugin({
18 | 'process.env': {
19 | 'NODE_ENV': "'production'"
20 | }
21 | }),
22 | new webpack.optimize.UglifyJsPlugin({
23 | compressor: {
24 | warnings: false
25 | }
26 | })
27 | ],
28 | module: {
29 | loaders: [
30 | // js
31 | {
32 | test: /\.js$/,
33 | loaders: ['babel'],
34 | include: path.join(__dirname, 'client')
35 | },
36 | // CSS
37 | {
38 | test: /\.styl$/,
39 | include: path.join(__dirname, 'client'),
40 | loader: 'style-loader!css-loader!stylus-loader'
41 | }
42 | ]
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Learn Redux Starter files
2 |
3 | This repo includes the `learn-redux` folder which is where you will build your application as well as a number of partially finished `client` folder contents that correspond with the **start** of each video. These stepped folders should be used to reference or restore your application if things aren't working 100%.
4 |
5 | There are only folders for videos which have significant changes.
6 |
7 | ## Getting Started
8 |
9 | Fork or download this repo and run `npm install` in the `learn-redux` folder with the `package.json` file.
10 |
11 |
12 | ## Pull Requests
13 |
14 | Pull requests that fix dependencies between the videos and this repo are welcome as long as they correspond with the code written in the videos.
15 |
16 |
17 | ## FAQ / Help
18 |
19 | **Before anything**, make sure you are on the latest node. You can run `node -v` and if you have anything less than `5.x`, you'll need to update. The files will probably work on older versions, but in my experience 80% of issues go away with an update.
20 |
21 | ### Q: I'm getting: _warning.js:44 Warning: Main: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop._
22 |
23 |
24 | **A:**: I haven't looked into why I didn't get this error, but it means that you should not pass down `key` or `ref` because those are props that are tied and unique to each element. For now, there is a [nice fix posted here](https://github.com/wesbos/Learn-Redux-Starter-Files/issues/6#issuecomment-222210005)
25 |
26 | ### Q: I'm getting Unexpected Token Error
27 |
28 | **A:** You probably don't have the `.babelrc` file in your `learn-redux` folder. This makes sure that you have all the right transpile dependencies. [Grab it from here](https://github.com/wesbos/Learn-Redux-Starter-Files/blob/master/learn-redux/.babelrc).
29 |
30 | ### Q: How do I download these videos?
31 |
32 | **A:** I made a video for [exactly this](https://www.youtube.com/watch?v=-eUd2k5M1B0).
33 |
34 | ### Q: What theme and font are you using?
35 |
36 | **A:** Cobalt2 and Operator Mono. I wrote a bit of info here →
37 |
38 |
39 |
--------------------------------------------------------------------------------