├── public
├── favicon.ico
├── manifest.json
└── index.html
├── React_MST_GraphQL_Logo.jpg
├── React_MST_GraphQL_Logo.psd
├── .gitignore
├── src
├── components
│ ├── header
│ │ ├── avatar.js
│ │ └── header.js
│ ├── repositories
│ │ ├── respositories.js
│ │ └── repository.js
│ ├── grid
│ │ └── grid.js
│ └── loading
│ │ └── loading.js
├── index.js
├── services
│ └── graphql.service.js
├── queries
│ └── viewer.query.js
├── containers
│ └── app
│ │ └── app.js
├── stylesheets
│ └── common.css
├── logo.svg
├── stores
│ └── github.js
└── registerServiceWorker.js
├── package.json
├── .github
└── FUNDING.yml
└── README.md
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexvcasillas/react-mobx-state-tree/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/React_MST_GraphQL_Logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexvcasillas/react-mobx-state-tree/HEAD/React_MST_GraphQL_Logo.jpg
--------------------------------------------------------------------------------
/React_MST_GraphQL_Logo.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexvcasillas/react-mobx-state-tree/HEAD/React_MST_GraphQL_Logo.psd
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | # firebase
24 | /firebase
25 |
--------------------------------------------------------------------------------
/src/components/header/avatar.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | const Avatar = styled.div`
4 | width: 120px;
5 | height: 120px;
6 | border-radius: 50%;
7 | background-image: url(${({ picture }) => picture});
8 | background-position: center;
9 | background-size: cover;
10 | border: 4px solid #ffffff;
11 | margin: 40px auto;
12 | box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.2);
13 | `;
14 |
15 | export default Avatar;
16 |
--------------------------------------------------------------------------------
/src/components/repositories/respositories.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | import Repository from './repository';
5 |
6 | const Wrapper = styled.div`
7 | width: 100%;
8 | max-width: 100%;
9 | margin: 0 auto;
10 | display: flex;
11 | flex-direction: row;
12 | flex-wrap: wrap;
13 | `;
14 |
15 | const Repositories = ({ repos }) =>
16 |
17 | {repos.map((repo, i) => )}
18 | ;
19 |
20 | export default Repositories;
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-mobx-state-tree",
3 | "version": "0.2.0",
4 | "private": true,
5 | "dependencies": {
6 | "apollo-boost": "^0.1.4",
7 | "graphql": "^0.13.2",
8 | "mobx": "^4.2.0",
9 | "mobx-react": "^5.0.0",
10 | "mobx-state-tree": "^2.0.4",
11 | "react": "^16.3.2",
12 | "react-dom": "^16.3.2",
13 | "react-scripts": "1.1.4",
14 | "styled-components": "^3.2.6"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test --env=jsdom",
20 | "eject": "react-scripts eject"
21 | }
22 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import { Provider } from 'mobx-react';
4 | import GithubStore from './stores/github';
5 | import registerServiceWorker from './registerServiceWorker';
6 | import App from './containers/app/app';
7 |
8 | import './stylesheets/common.css';
9 |
10 | const githubStore = GithubStore.create();
11 | githubStore.fetchFromGithub();
12 |
13 | const store = {
14 | github: githubStore
15 | };
16 |
17 | const router = (
18 |
19 |
22 |
23 | );
24 |
25 | render(router, document.getElementById('root'));
26 | registerServiceWorker();
27 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: alexvcasillas
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/src/services/graphql.service.js:
--------------------------------------------------------------------------------
1 | import ApolloClient, { gql } from 'apollo-boost';
2 |
3 | const githubToken = 'your_token_here';
4 |
5 | const client = new ApolloClient({
6 | uri: 'https://api.github.com/graphql',
7 | fetchOptions: {
8 | credentials: 'include'
9 | },
10 | request: (operation) => {
11 | operation.setContext({
12 | headers: {
13 | authorization: `Bearer ${githubToken}`
14 | }
15 | });
16 | },
17 | onError: ({ graphQLErrors, networkError }) => {
18 | if (graphQLErrors) return console.error('GrahQL Errors:', graphQLErrors);
19 | if (networkError) return console.error('Network Error: ', networkError);
20 | }
21 | });
22 |
23 | export { client, gql };
--------------------------------------------------------------------------------
/src/queries/viewer.query.js:
--------------------------------------------------------------------------------
1 | import { gql } from '../services/graphql.service';
2 |
3 | export const viewerQuery = gql`
4 | query {
5 | viewer {
6 | avatarUrl
7 | bio
8 | login
9 | name
10 | followers {
11 | totalCount
12 | }
13 | following {
14 | totalCount
15 | }
16 | websiteUrl
17 | url
18 | repositories(first: 100) {
19 | totalCount
20 | nodes {
21 | name
22 | description
23 | url
24 | homepageUrl
25 | isFork
26 | createdAt
27 | updatedAt
28 | stargazers {
29 | totalCount
30 | }
31 | }
32 | }
33 | }
34 | }
35 | `;
--------------------------------------------------------------------------------
/src/components/grid/grid.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | const Grid = styled.div`
4 | display: flex;
5 | flex-direction: row;
6 | flex-wrap: nowrap;
7 | margin-top: ${({ margins }) => (margins && margins.top ? margins.top : 0)}px;
8 | margin-bottom: ${({ margins }) =>
9 | margins && margins.bottom ? margins.bottom : 0}px;
10 | margin-left: ${({ margins }) =>
11 | margins && margins.left ? margins.left : 0}px;
12 | margin-right: ${({ margins }) =>
13 | margins && margins.right ? margins.right : 0}px;
14 | `;
15 |
16 | const Column = styled.div`
17 | width: ${({ size }) =>
18 | size === 2 ? '50%' : size === 3 ? '33.333%' : size === 4 ? '25%' : '100%'};
19 | margin-right: ${({ margin }) =>
20 | margin && margin.right ? margin.right : '10'}px;
21 | &:last-child {
22 | margin-right: 0;
23 | }
24 | `;
25 |
26 | export default Grid;
27 | export { Column };
28 |
--------------------------------------------------------------------------------
/src/containers/app/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { inject, observer } from 'mobx-react';
3 | import styled from 'styled-components';
4 |
5 | import Loading from '../../components/loading/loading';
6 | import Header from '../../components/header/header';
7 | import Repositories from '../../components/repositories/respositories';
8 |
9 | const defaultName = 'Alex Casillas';
10 | const defaultAvatar =
11 | 'https://avatars0.githubusercontent.com/u/9496960?v=4&s=460';
12 |
13 | const Wrapper = styled.div`
14 | width: 1200px;
15 | max-width: 100%;
16 | margin: 0 auto;
17 | margin-bottom: 50px;
18 | `;
19 |
20 | const App = ({ github }) =>
21 | github.fetchingData
22 | ?
23 | :
24 |
30 | {github.repos.length ? : null}
31 | ;
32 |
33 | export default inject('github')(observer(App));
34 |
--------------------------------------------------------------------------------
/src/components/loading/loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled, { keyframes } from 'styled-components';
3 |
4 | const foregroundCircle = keyframes`
5 | 0% {
6 | ransform: scale(0.3) rotate(0deg);
7 | }
8 | 12.5% {
9 | transform: scale(0.3) rotate(180deg);
10 | }
11 | 25%, 50% {
12 | opacity: 1;
13 | }
14 | 50% {
15 | transform: scale(1) rotate(720deg);
16 | }
17 | 100% {
18 | transform: scale(0.3) rotate(1800deg);
19 | opacity: 0.5;
20 | }
21 | `;
22 |
23 | const backgroundCircle = keyframes`
24 | 12.5% {
25 | transform: scale(0.3);
26 | }
27 | 90%, 100% {
28 | transform: scale(2);
29 | opacity: 0;
30 | }
31 | `;
32 |
33 | const Loader = styled.div`
34 | width: 100px;
35 | height: 100px;
36 | position: relative;
37 | margin: auto;
38 |
39 | &:before,
40 | &:after {
41 | content: '';
42 | position: absolute;
43 | border-radius: 50%;
44 | width: 100%;
45 | height: 100%;
46 | top: 0;
47 | left: 0;
48 |
49 | animation-duration: 3s;
50 |
51 | animation-timing-function: linear;
52 |
53 | animation-iteration-count: infinite;
54 | }
55 |
56 | &:before {
57 | top: -2px;
58 | left: -2px;
59 | border-style: solid;
60 | border-width: 3px 3px 3px 0;
61 | border-color: #fff transparent transparent;
62 | transform: scale(0.3) rotate(0deg);
63 | opacity: 0.5;
64 | animation-name: ${foregroundCircle};
65 | }
66 |
67 | &:after {
68 | background: #fff;
69 | opacity: 0.5;
70 | transform: scale(0);
71 | animation-name: ${backgroundCircle};
72 | }
73 | `;
74 |
75 | const Wrapper = styled.div`
76 | width: 100%;
77 | height: 100%;
78 | display: flex;
79 | flex-direction: column;
80 | align-items: center;
81 | justify-content: center;
82 | position: absolute;
83 | top: 0;
84 | left: 0;
85 | background-image: linear-gradient(to bottom left, #48c6ef 0%, #6f86d6 100%);
86 | `;
87 |
88 | const Loading = () =>
89 |
90 |
91 | ;
92 |
93 | export default Loading;
94 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
28 | Alex Casillas - @alexvcasillas
29 |
30 |
31 |
32 |
35 |
36 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/stylesheets/common.css:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/
2 | v2.0 | 20110126
3 | License: none (public domain)
4 | */
5 | a,
6 | abbr,
7 | acronym,
8 | address,
9 | applet,
10 | article,
11 | aside,
12 | audio,
13 | b,
14 | big,
15 | blockquote,
16 | body,
17 | canvas,
18 | caption,
19 | center,
20 | cite,
21 | code,
22 | dd,
23 | del,
24 | details,
25 | dfn,
26 | div,
27 | dl,
28 | dt,
29 | em,
30 | embed,
31 | fieldset,
32 | figcaption,
33 | figure,
34 | footer,
35 | form,
36 | h1,
37 | h2,
38 | h3,
39 | h4,
40 | h5,
41 | h6,
42 | header,
43 | hgroup,
44 | html,
45 | i,
46 | iframe,
47 | img,
48 | ins,
49 | kbd,
50 | label,
51 | legend,
52 | li,
53 | mark,
54 | menu,
55 | nav,
56 | object,
57 | ol,
58 | output,
59 | p,
60 | pre,
61 | q,
62 | ruby,
63 | s,
64 | samp,
65 | section,
66 | small,
67 | span,
68 | strike,
69 | strong,
70 | sub,
71 | summary,
72 | sup,
73 | table,
74 | tbody,
75 | td,
76 | tfoot,
77 | th,
78 | thead,
79 | time,
80 | tr,
81 | tt,
82 | u,
83 | ul,
84 | var,
85 | video {
86 | margin: 0;
87 | padding: 0;
88 | border: 0;
89 | font-size: 100%;
90 | font: inherit;
91 | vertical-align: baseline;
92 | }
93 |
94 | /* HTML5 display-role reset for older browsers */
95 | article,
96 | aside,
97 | details,
98 | figcaption,
99 | figure,
100 | footer,
101 | header,
102 | hgroup,
103 | menu,
104 | nav,
105 | section {
106 | display: block;
107 | }
108 |
109 | body {
110 | line-height: 1;
111 | }
112 |
113 | ol,
114 | ul {
115 | list-style: none;
116 | }
117 |
118 | blockquote,
119 | q {
120 | quotes: none;
121 | }
122 |
123 | blockquote:after,
124 | blockquote:before,
125 | q:after,
126 | q:before {
127 | content: '';
128 | content: none;
129 | }
130 |
131 | table {
132 | border-collapse: collapse;
133 | border-spacing: 0;
134 | }
135 |
136 | .pointer {
137 | cursor: pointer;
138 | }
139 |
140 | * {
141 | margin: 0;
142 | padding: 0;
143 | box-sizing: border-box;
144 | }
145 |
146 | body {
147 | font-family: 'Open Sans', sans-serif;
148 | font-weight: 400;
149 | background-image: linear-gradient(to bottom left, #48c6ef 0%, #6f86d6 100%);
150 | color: white;
151 | }
152 |
153 | strong {
154 | font-weight: 600;
155 | }
156 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/stores/github.js:
--------------------------------------------------------------------------------
1 | import { types, flow } from 'mobx-state-tree';
2 | import { client } from '../services/graphql.service';
3 | import { viewerQuery } from '../queries/viewer.query';
4 |
5 | function compareRepo(a, b) {
6 | const aUpdated = new Date(a.updatedAt);
7 | const bUpdated = new Date(b.updatedAt);
8 | if (aUpdated > bUpdated) {
9 | return -1;
10 | }
11 | if (aUpdated < bUpdated) {
12 | return 1;
13 | }
14 | return 0;
15 | }
16 |
17 | const UserModel = types
18 | .model('UserModel', {
19 | name: types.maybe(types.string),
20 | bio: types.maybe(types.string),
21 | avatar: types.maybe(types.string),
22 | followers: types.maybe(types.number),
23 | following: types.maybe(types.number)
24 | })
25 | .views(self => ({}))
26 | .actions(self => ({}));
27 |
28 | const RepositoryModel = types
29 | .model('RepoModel', {
30 | name: types.string,
31 | description: types.maybe(types.string),
32 | url: types.maybe(types.string),
33 | homepageUrl: types.maybe(types.string),
34 | isFork: types.maybe(types.boolean),
35 | stargazers: types.optional(types.frozen, null),
36 | createdAt: types.maybe(types.string),
37 | updatedAt: types.maybe(types.string)
38 | })
39 | .views(self => ({}))
40 | .actions(self => ({}));
41 |
42 | const GithubStore = types
43 | .model('GithubStore', {
44 | searchName: types.optional(types.string, ''),
45 | user: types.optional(types.maybe(UserModel), null), // Object with all the user data that comes from the Github API Fetch
46 | repos: types.optional(types.array(RepositoryModel), []), // Array of Repositories that comes from the Github API Fetch
47 | fetchingData: types.optional(types.boolean, false)
48 | })
49 | .views(self => ({
50 | get AmountOfRepos() {
51 | return this.repos.length;
52 | }
53 | }))
54 | .actions(self => {
55 | const fetchFromGithub = flow(function* () {
56 | self.fetchingData = true;
57 | const { data: { viewer } } = yield client.query({
58 | query: viewerQuery,
59 | fetchPolicy: 'network-only'
60 | });
61 | self.user = UserModel.create({
62 | name: viewer.name,
63 | bio: viewer.bio,
64 | avatar: viewer.avatarUrl,
65 | followers: viewer.followers.totalCount,
66 | following: viewer.following.totalCount
67 | });
68 | let repoOrder = [...viewer.repositories.nodes];
69 | repoOrder = repoOrder.sort(compareRepo);
70 | self.repos = repoOrder;
71 | self.fetchingData = false;
72 | });
73 | return { fetchFromGithub };
74 | });
75 |
76 | export default GithubStore;
77 |
--------------------------------------------------------------------------------
/src/components/repositories/repository.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const Wrapper = styled.div`
5 | width: 50%;
6 | padding: 20px;
7 |
8 | @media (max-width: 768px) {
9 | width: 100%;
10 | }
11 | `;
12 |
13 | const Card = styled.div`
14 | background-color: #ffffff;
15 | box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.2);
16 | color: #1e1e1e;
17 | border-radius: 4px;
18 | padding: 20px;
19 | `;
20 |
21 | const Title = styled.div`
22 | display: block;
23 | font-weight: 600;
24 | margin-bottom: 10px;
25 | max-width: 80%;
26 | white-space: nowrap;
27 | overflow: hidden;
28 | text-overflow: ellipsis;
29 | padding-bottom: 5px;
30 | margin-bottom: 10px;
31 | `;
32 |
33 | const Fork = styled.span`
34 | display: inline-block;
35 | margin-right: 10px;
36 | color: #979797;
37 | `;
38 |
39 | const Link = styled.a`
40 | display: inline-block;
41 | text-decoration: none;
42 | outline: none;
43 | color: #6f86d6;
44 | transition: color 0.3s ease-in-out;
45 |
46 | &:hover {
47 | text-decoration: none;
48 | outline: none;
49 | color: #48c6ef;
50 | }
51 | `;
52 |
53 | const Homepage = styled.a`
54 | max-width: 100%;
55 | display: inline-block;
56 | text-decoration: none;
57 | outline: none;
58 | color: #6f86d6;
59 | transition: color 0.3s ease-in-out;
60 |
61 | &:hover {
62 | text-decoration: none;
63 | outline: none;
64 | color: #48c6ef;
65 | }
66 | `;
67 |
68 | const Description = styled.div`
69 | line-height: 1.4;
70 | white-space: nowrap;
71 | overflow: hidden;
72 | text-overflow: ellipsis;
73 | text-decoration: none;
74 | `;
75 |
76 | const Block = styled.div`
77 | width: 100%;
78 | display: flex;
79 | flex-direction: row;
80 | margin-top: 10px;
81 |
82 | strong {
83 | margin-right: 5px;
84 | }
85 | `;
86 |
87 | const Repository = ({ repository }) => (
88 |
89 |
90 |
91 | {repository.isFork ? Forked : ''}
92 |
93 | {repository.name}
94 |
95 |
96 | {repository.description}
97 |
98 | Homepage:
99 |
100 | {repository.homepageUrl}
101 |
102 |
103 |
104 |
105 |
106 | 🌟
107 | {' '}
108 | Stargrazzers:{' '}
109 | {' '}
110 | {repository.stargazers.totalCount}
111 |
112 |
113 | Created at: {' '}
114 | {new Date(repository.createdAt).toLocaleString()}
115 |
116 |
117 | Last update at: {' '}
118 | {new Date(repository.updatedAt).toLocaleString()}
119 |
120 |
121 |
122 | );
123 |
124 | export default Repository;
125 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ### About React + MobX State Tree & GraphQL
4 |
5 | This project was initially built as a personal project to create my own website ([https://alexvcasillas.com](https://alexvcasillas.com)) to test the latest features of the awesome [MobX State Tree](https://github.com/mobxjs/mobx-state-tree) and [GraphQL](https://github.com/facebook/graphql). The React part of this project is based on the latest [Create React App](https://github.com/facebookincubator/create-react-app) stable release (check their docs for further knowledge).
6 |
7 | ### Purpose
8 |
9 | The purpose of this project is only intended for learning. It's not intended to be a commercial product or any other related kind of stuff. This is Open Source so you should feel free to use it and modify it as you wanted. In fact, I encourage you to modify the structure or how I do things to fit your needs and the way you handle things, just set it to be comfortable to you.
10 |
11 | ### How To
12 |
13 | If you want to try this project the first thing you have to do is to **clone** this repository. How? Just go to your favourite terminal and execute the following command (assuming that you previously have **git** installed): `git clone https://github.com/alexvcasillas/react-mobx-state-tree.git`. By doing so, you'll have the repository cloned in your computer. The next step is to move inside by typing `cd react-mobx-state-tree`. Alright, we're in now! You can't run it at this point because you have no dependencies installed so, we need to start with that, run the following command: `npm install` or if you're a yarn user just `yarn`.
14 | This might take a while because it's downloading all the minimum requirements as local dependencies so you can run this project (they're just a few). When it's done, you're good to go, simply run the following command `npm start` or if you're a yarn user `yarn start` and you will see the development build process and when everything is done, your browser will open a new window/tab with this project up and running.
15 |
16 | If you want your Github profile to fill this project, you need to generate a Github's Personal Access Token (PAT) and go to `src/utils/graphql.js - line 10` and replace `const githubToken = 'your_token_here';` the content within single quotes with your Personal Access Token (PAT). Then just make the build and deploy it somewhere or just run it as development.
17 |
18 | You can customize everything with Styled Components!
19 |
20 | ### TLDR: How To
21 |
22 | 1. `git clone https://github.com/alexvcasillas/react-mobx-state-tree.git`
23 | 2. `cd react-mobx-state-tree`
24 | 3. `npm install` or `yarn`
25 | 4. `npm start` or `yarn start`
26 |
27 | ### Production Builds
28 |
29 | Because this project is based on [Create React App](https://github.com/facebookincubator/create-react-app), you have all the CRA available commands and the build process is delegated to them. Simply run `npm run build` or `yarn build` and the build process will being and, at the end of it, you'll have a `dist` folder with your SPA ready to be deployed as a static website at any host.
30 |
31 | ### Built with
32 |
33 | This project is being used in:
34 |
35 | [https://alexvcasillas.com](https://alexvcasillas.com)
36 |
37 | ### Contributions
38 |
39 | I'm always looking to improve this project so, if you feel like you can contribute to it to improve any of it's features, just fork it and make a proper Pull Request. I'll be so glad to check it with you and merge it into the master project.
40 |
41 | ### Credits
42 |
43 | I want to thank the following people and organizations for creating all that this project is based on:
44 |
45 | [Michel Weststrate](https://github.com/mweststrate) for the incredible job creating MobX and MobX State Tree.
46 |
47 | [Mattia Manzati](https://github.com/mattiamanzati) for it's support with everything related to MobX State Tree.
48 |
49 | [Max Stoiber](https://github.com/mxstbr) for it's awesome Styled Components :nail_care:
50 |
51 | [Dan Abramov](https://github.com/gaearon) for creating Create React App (also all of the contributors to it!)
52 |
53 | [Apollo GraphQL Team](https://github.com/apollographql) for their awesome contribution with Apollo Client.
54 |
--------------------------------------------------------------------------------
/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (!isLocalhost) {
36 | // Is not local host. Just register service worker
37 | registerValidSW(swUrl);
38 | } else {
39 | // This is running on localhost. Lets check if a service worker still exists or not.
40 | checkValidServiceWorker(swUrl);
41 | }
42 | });
43 | }
44 | }
45 |
46 | function registerValidSW(swUrl) {
47 | navigator.serviceWorker
48 | .register(swUrl)
49 | .then(registration => {
50 | registration.onupdatefound = () => {
51 | const installingWorker = registration.installing;
52 | installingWorker.onstatechange = () => {
53 | if (installingWorker.state === 'installed') {
54 | if (navigator.serviceWorker.controller) {
55 | // At this point, the old content will have been purged and
56 | // the fresh content will have been added to the cache.
57 | // It's the perfect time to display a "New content is
58 | // available; please refresh." message in your web app.
59 | console.log('New content is available; please refresh.');
60 | } else {
61 | // At this point, everything has been precached.
62 | // It's the perfect time to display a
63 | // "Content is cached for offline use." message.
64 | console.log('Content is cached for offline use.');
65 | }
66 | }
67 | };
68 | };
69 | })
70 | .catch(error => {
71 | console.error('Error during service worker registration:', error);
72 | });
73 | }
74 |
75 | function checkValidServiceWorker(swUrl) {
76 | // Check if the service worker can be found. If it can't reload the page.
77 | fetch(swUrl)
78 | .then(response => {
79 | // Ensure service worker exists, and that we really are getting a JS file.
80 | if (
81 | response.status === 404 ||
82 | response.headers.get('content-type').indexOf('javascript') === -1
83 | ) {
84 | // No service worker found. Probably a different app. Reload the page.
85 | navigator.serviceWorker.ready.then(registration => {
86 | registration.unregister().then(() => {
87 | window.location.reload();
88 | });
89 | });
90 | } else {
91 | // Service worker found. Proceed as normal.
92 | registerValidSW(swUrl);
93 | }
94 | })
95 | .catch(() => {
96 | console.log(
97 | 'No internet connection found. App is running in offline mode.'
98 | );
99 | });
100 | }
101 |
102 | export function unregister() {
103 | if ('serviceWorker' in navigator) {
104 | navigator.serviceWorker.ready.then(registration => {
105 | registration.unregister();
106 | });
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/components/header/header.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | import Grid, { Column } from '../grid/grid';
5 |
6 | import Avatar from './avatar';
7 |
8 | const Wrapper = styled.div`
9 | width: 600px;
10 | max-width: 80%;
11 | margin: 0 auto;
12 | margin-bottom: 40px;
13 | `;
14 |
15 | const Name = styled.div`
16 | text-align: center;
17 | margin-bottom: 20px;
18 | `;
19 |
20 | const Jump = styled.span`
21 | display: inline-block;
22 | transition: transform 0.2s;
23 | cursor: default;
24 | min-width: 20px;
25 | text-shadow: 3px 3px 0 rgba(0, 0, 0, 0.2);
26 | font-size: 50px;
27 | text-transform: uppercase;
28 | font-weight: 600;
29 |
30 | @media (max-width: 768px) {
31 | font-size: 20px;
32 | }
33 |
34 | &:hover {
35 | transform: translateY(-20px) rotate(10deg) scale(2);
36 | }
37 | `;
38 |
39 | const Title = styled.div`
40 | font-weight: 600;
41 | margin-bottom: 10px;
42 | `;
43 |
44 | const Amount = styled.div`
45 | font-size: 40px;
46 | font-family: 'Pacifico', sans-serif;
47 | color: ${({ count }) => (count && count >= 100 ? '#ffc600' : 'inherit')};
48 | `;
49 |
50 | const FollowBox = styled.div`
51 | width: 100%;
52 | padding: 20px;
53 | background-color: #ffffff;
54 | box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.2);
55 | color: #1e1e1e;
56 | border-radius: 4px;
57 | text-align: center;
58 | `;
59 |
60 | const Social = styled.div`margin: 40px 0;`;
61 |
62 | const SVG = styled.svg`
63 | transition: transform 0.2s;
64 | &:hover {
65 | transform: scale(1.5);
66 | }
67 | `;
68 |
69 | const Header = ({ title, avatar, followingCount, followersCount }) =>
70 |
71 |
72 |
73 | {[...title].map((letter, i) =>
74 |
75 | {letter}
76 |
77 | )}
78 |
79 |
80 |
81 |
82 |
87 |
93 |
94 |
95 |
96 |
101 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | Followers
117 |
118 | {followersCount}
119 |
120 |
121 |
122 | Following
123 |
124 | {followingCount}
125 |
126 |
127 |
128 |
129 | ;
130 |
131 | export default Header;
132 |
--------------------------------------------------------------------------------