├── .gitignore ├── .idea ├── .gitignore ├── inspectionProfiles │ └── Project_Default.xml ├── instadev.iml ├── jsLibraryMappings.xml ├── misc.xml ├── modules.xml ├── vcs.xml └── webResources.xml ├── README.md ├── docs └── instadev.gif ├── package.json ├── public ├── _redirects ├── favicon.ico ├── favicon.png ├── fontawesome.min.css ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json ├── normalize.css ├── robots.txt └── webfonts │ ├── fa-brands-400.eot │ ├── fa-brands-400.svg │ ├── fa-brands-400.ttf │ ├── fa-brands-400.woff │ ├── fa-brands-400.woff2 │ ├── fa-regular-400.eot │ ├── fa-regular-400.svg │ ├── fa-regular-400.ttf │ ├── fa-regular-400.woff │ ├── fa-regular-400.woff2 │ ├── fa-solid-900.eot │ ├── fa-solid-900.svg │ ├── fa-solid-900.ttf │ ├── fa-solid-900.woff │ └── fa-solid-900.woff2 ├── src ├── __tests__ │ ├── mocks │ │ ├── comments.json │ │ ├── likes.json │ │ ├── posts.json │ │ ├── stories.json │ │ └── users.json │ └── validation.spec.js ├── assets │ └── img │ │ ├── instagram-glyph.png │ │ └── instagram-logo.svg ├── components │ ├── Loading │ │ ├── Loading.jsx │ │ ├── Loading.scss │ │ └── index.jsx │ ├── Post │ │ ├── Post.jsx │ │ ├── Post.scss │ │ └── index.jsx │ ├── Story │ │ ├── Story.jsx │ │ ├── Story.scss │ │ └── index.jsx │ ├── SuccessMessage │ │ ├── SuccessMessage.jsx │ │ ├── SuccessMessage.scss │ │ └── index.jsx │ ├── Topbar │ │ ├── Topbar.jsx │ │ ├── Topbar.scss │ │ └── index.jsx │ └── User │ │ ├── User.jsx │ │ └── index.jsx ├── containers │ ├── App │ │ ├── App.jsx │ │ ├── App.scss │ │ └── index.jsx │ ├── Posts │ │ ├── Posts.jsx │ │ └── index.jsx │ ├── Stories │ │ ├── Stories.jsx │ │ ├── Stories.scss │ │ └── index.jsx │ ├── UserForm │ │ ├── UserForm.jsx │ │ ├── UserForm.scss │ │ └── index.jsx │ ├── UserPosts │ │ ├── UserPosts.jsx │ │ ├── UserPosts.scss │ │ └── index.jsx │ ├── UserProfile │ │ ├── UserProfile.jsx │ │ ├── UserProfile.scss │ │ └── index.jsx │ └── UsersList │ │ ├── UsersList.jsx │ │ ├── UsersList.scss │ │ └── index.jsx ├── index.js ├── modules │ ├── enpoints.js │ ├── formatter.js │ └── request.js ├── routes │ ├── FeedRoute │ │ ├── FeedRoute.jsx │ │ ├── FeedRoute.scss │ │ └── index.jsx │ ├── NewUserRoute │ │ ├── NewUserRoute.jsx │ │ └── index.jsx │ ├── ProfileRoute │ │ ├── ProfileRoute.jsx │ │ └── index.jsx │ ├── UsersRoute │ │ ├── UsersRoute.jsx │ │ └── index.jsx │ └── index.jsx ├── serviceWorker.js └── setupTests.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml 3 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/instadev.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/jsLibraryMappings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/webResources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Instadev React SPA 2 | [![Netlify Status](https://api.netlify.com/api/v1/badges/dd4dcad5-a68d-441e-b3ac-220485b75d7e/deploy-status)](https://app.netlify.com/sites/viniciusvinna-react-instagram/deploys) 3 | 4 | ![](docs/instadev.gif) 5 | 6 | ## Objetivo: 7 | O desafio desta semana e da próxima será desenvolver um `SPA (Single Page Application)` mobile-first do **Instagram** em React, consumindo uma Rest API que deverá cumprir os seguintes critérios: 8 | 9 | > * Ser desenvolvido utilizando abordagem funcional e React Hooks para gerenciamento de ciclo de vida e estados. 10 | > * Consumir os dados da Rest API, usando o [Fetch API](https://developer.mozilla.org/pt-BR/docs/Web/API/Fetch_API/Using_Fetch) do Javascript. 11 | > * Exibir os **posts** de todos os usuários na rota inicial `/`. 12 | > * Exibir todos os **usuários** cadastrados na rota `/users` 13 | > * Exibir o perfil do **usuário** e seus respectivos **posts** na rota `/users/{username}` 14 | > * Exibir os **stories** dos **usuários** na rota inicial `/`. 15 | > * Ao clicar no ícone de **story** do **usuário** deverá abrir seu story com o respectivo vídeo e barra de progresso com a opção de fechar e voltar para o feed (rota inicial). 16 | > * Deve permitir cadastrar um usuário na rota `/newuser` e exibir uma mensagem de alerta ao enviar. 17 | 18 | ### Referência em Produção: 19 | [https://viniciusvinna.netlify.app/react-instagram](https://viniciusvinna.netlify.app/react-instagram) 20 | 21 | ### Repositório Base (para fins de estudo): 22 | [https://github.com/ViniciusVinna/react-instagram](https://github.com/ViniciusVinna/react-instagram) 23 | 24 | ## Requisitos Parte 1: 25 | * Nesta primeira parte você deverá focar apenas na componentização do projeto seguindo os padrões descritos abaixo na seção `Estrutura de Arquivos e Pastas`. 26 | * Não se preocupe com lógica de estados ou requisições http nesta parte, adicione os dados do usuário `hard-code`. 27 | 28 | ### Estrutura de Arquivos e Pastas: 29 | Como já aprendemos em aula, a organização das pastas e arquivos deverá utilizar os seguintes padrões: 30 | * `presentational components` e `container components` -> [Referência](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0) 31 | * `Rails-Style Structure + Module Index` -> [Referência](https://webcache.googleusercontent.com/search?q=cache:DZ0HZwEl7AUJ:https://www.learnhowtoprogram.com/fidgetech-4-react/4-4-advanced-topics/4-4-3-3-react-and-redux-design-patterns+&cd=1&hl=en&ct=clnk&gl=br) 32 | 33 | Seguindo estes dois padrões, a estrutura de arquivos e pastas do seu projeto deverá ficar exatamente assim (dentro de `/src`: 34 | ```bash 35 | ├── assets 36 | │   └── img 37 | │   ├── instagram-glyph.png 38 | │   └── instagram-logo.svg 39 | ├── components 40 | │   ├── Loading 41 | │   │   ├── Loading.jsx 42 | │   │   ├── Loading.scss 43 | │   │   └── index.jsx 44 | │   ├── Post 45 | │   │   ├── Post.jsx 46 | │   │   ├── Post.scss 47 | │   │   └── index.jsx 48 | │   ├── Story 49 | │   │   ├── Story.jsx 50 | │   │   ├── Story.scss 51 | │   │   └── index.jsx 52 | │   ├── SuccessMessage 53 | │   │   ├── SuccessMessage.jsx 54 | │   │   ├── SuccessMessage.scss 55 | │   │   └── index.jsx 56 | │   ├── Topbar 57 | │   │   ├── Topbar.jsx 58 | │   │   ├── Topbar.scss 59 | │   │   └── index.jsx 60 | │   └── User 61 | │   ├── User.jsx 62 | │   └── index.jsx 63 | ├── containers 64 | │   ├── App 65 | │   │   ├── App.jsx 66 | │   │   ├── App.scss 67 | │   │   └── index.jsx 68 | │   ├── Posts 69 | │   │   ├── Posts.jsx 70 | │   │   └── index.jsx 71 | │   ├── Stories 72 | │   │   ├── Stories.jsx 73 | │   │   ├── Stories.scss 74 | │   │   └── index.jsx 75 | │   ├── UserForm 76 | │   │   ├── UserForm.jsx 77 | │   │   ├── UserForm.scss 78 | │   │   └── index.jsx 79 | │   ├── UserPosts 80 | │   │   ├── UserPosts.jsx 81 | │   │   ├── UserPosts.scss 82 | │   │   └── index.jsx 83 | │   ├── UserProfile 84 | │   │   ├── UserProfile.jsx 85 | │   │   ├── UserProfile.scss 86 | │   │   └── index.jsx 87 | │   └── UsersList 88 | │   ├── UsersList.jsx 89 | │   ├── UsersList.scss 90 | │   └── index.jsx 91 | ├── index.js 92 | ├── modules 93 | ├── routes 94 | │   ├── FeedRoute 95 | │   │   ├── FeedRoute.jsx 96 | │   │   ├── FeedRoute.scss 97 | │   │   └── index.jsx 98 | │   ├── NewUserRoute 99 | │   │   ├── NewUserRoute.jsx 100 | │   │   └── index.jsx 101 | │   ├── ProfileRoute 102 | │   │   ├── ProfileRoute.jsx 103 | │   │   └── index.jsx 104 | │   ├── UsersRoute 105 | │   │   ├── UsersRoute.jsx 106 | │   │   └── index.jsx 107 | │   └── index.jsx 108 | ├── serviceWorker.js 109 | └── setupTests.js 110 | ``` 111 | 112 | ### IMPORTANTE: 113 | Nesta fase você já foi apresentado ao [testing-library](https://testing-library.com/docs/intro) e para que você seja avaliado corretamente ao submeter seu desafio, é necessário que **TODOS** os seus **elementos React**, como `componentes`, `containers` e `rotas` possuam o atributo JSX/HTML (sintético) `data-testid="{nome-do-componente}"`: 114 | 115 | ```bash 116 | ├── components 117 | │   ├── Loading 118 | │   │   └── Loading.jsx -> data-testid="loading" 119 | │   ├── Post 120 | │   │   └── Post.jsx -> data-testid="post" 121 | │   ├── Story 122 | │   │   └── Story.jsx -> data-testid="story" 123 | │   ├── SuccessMessage 124 | │   │   └── SuccessMessage.jsx -> data-testid="success-message" 125 | │   ├── Topbar 126 | │   │   └── Topbar.jsx -> data-testid="topbar" 127 | │   └── User 128 | │   └── User.jsx -> data-testid="user" 129 | ├── containers 130 | │   ├── App 131 | │   │   └── App.jsx -> data-testid="app" 132 | │   ├── Posts 133 | │   │   └── Posts.jsx -> data-testid="posts" 134 | │   ├── Stories 135 | │   │   └── Stories.jsx -> data-testid="stories" 136 | │   ├── UserForm 137 | │   │   └── UserForm.jsx -> data-testid="user-form" 138 | │   ├── UserPosts 139 | │   │   └── UserPosts.jsx -> data-testid="user-posts" 140 | │   ├── UserProfile 141 | │   │   └── UserProfile.jsx -> data-testid="user-profile" 142 | │   └── UsersList 143 | │   └── UsersList.jsx -> data-testid="user-list" 144 | └── routes 145 |   ├── FeedRoute 146 |    │   └── FeedRoute.jsx -> data-testid="feed-route" 147 |    ├── NewUserRoute 148 |   │   └── NewUserRoute.jsx -> data-testid="new-user-route" 149 |    ├── ProfileRoute 150 |    │   └── ProfileRoute.jsx -> data-testid="profile-route" 151 |    └── UsersRoute 152 |       └── UsersRoute.jsx -> data-testid="users-route" 153 | ``` 154 | 155 | ### Iniciar o projeto: 156 | * Instale as dependências do projeto com o comando `yarn install` ou `npm install`. 157 | * Inicie o projeto com comando `yarn start` ou `npm start` / `npm run start`. 158 | 159 | ## REST API 160 | ### Usuários 161 | | Resource | Method | Endpoint | Status Code | Response | 162 | |:--------:|:---------:|--------------------------------------------------------------|:-----------:|:---------------:| 163 | | Users | GET | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users | 200 | Array of Object | 164 | | User | GET | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/:id | 200 | Object | 165 | | User | POST | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users | 201 | Created object | 166 | | User | PUT | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/:id | 200 | Updated object | 167 | | User | DELETE | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/:id | 200 | Deleted object | 168 | 169 | > Body (POST / PUT): 170 | ```json 171 | { 172 | "name": "string", 173 | "avatar": "string", 174 | "username": "string", 175 | "email": "string" 176 | } 177 | ``` 178 | 179 | ### Stories 180 | | Resource | Method | Endpoint | Status Code | Response | 181 | |:--------:|:---------:|-----------------------------------------------------------------|:-----------:|:---------------:| 182 | | stories | GET | https://5e7d0266a917d70016684219.mockapi.io/api/v1/stories | 200 | Array of Object | 183 | | story | GET | https://5e7d0266a917d70016684219.mockapi.io/api/v1/stories/:id | 200 | Object | 184 | 185 | ### Posts 186 | | Resource | Method | Endpoint | Status Code | Response | 187 | |:--------:|:-------:|------------------------------------------------------------------------|-------------|-----------------| 188 | | Posts | GET | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/:id/posts | 200 | Array of Object | 189 | | Post | GET | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/:id/posts/:id | 200 | Object | 190 | | Post | POST | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/:id/posts | 201 | Created object | 191 | | Post | PUT | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/:id/posts/:id | 200 | Updated object | 192 | | Post | DELETE | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/:id/posts/:id | 200 | Deleted object | 193 | 194 | > Body (POST / PUT): 195 | ```json 196 | { 197 | "userId": "string", 198 | "imageUrl": "string" 199 | } 200 | ``` 201 | 202 | ### Comentários 203 | | Resource | Method | Endpoint | Status Code | Response | 204 | |:--------:|:-------:|----------------------------------------------------------------------------------------|-------------|-----------------| 205 | | Comments | GET | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/:id/posts/:id/comments | 200 | Array of Object | 206 | | Comment | GET | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/:id/posts/:id/comments/:id | 200 | Object | 207 | | Comment | POST | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/:id/posts/:id/comments | 201 | Created object | 208 | | Comment | PUT | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/:id/posts/:id/comments/:id | 200 | Updated object | 209 | | Comment | DELETE | https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/:id/posts/:id/comments/:id | 200 | Deleted object | 210 | 211 | > Body (POST / PUT): 212 | ```json 213 | { 214 | "postId": "string", 215 | "comment": "string", 216 | "avatar": "string", 217 | "name": "string" 218 | } 219 | ``` 220 | 221 | ### Query Params 222 | > Adicione os seguintes **queries** às requisições `GET`: 223 | > #### Paginação 224 | > `?page=1&limit=10` ou `?p=1&l=10` 225 | > 226 | > #### Ordenação 227 | > `?sortBy=createdAt&order=desc` 228 | > também é possível utilizar `sortby`, `orderBy`, ou `orderby` 229 | > OBS: se você omitir o parâmetro `order`, a ordenação padrão será 'asc' 230 | > 231 | > #### Busca 232 | > `?search=blog1` ou `?filter=blog1` 233 | 234 | ### Códigos de erro e mensagens de retorno 235 | * `200` - OK 236 | * `201` - OK 237 | * `404` - Not found 238 | * `500` - Server error 239 | 240 | ## Tópicos: 241 | Neste desafio você vai praticar os seus conhecimentos em: 242 | - **Fetch API** 243 | - **JS Funcional:** 244 | - **Modularização** 245 | - **Presentational e Container Components** 246 | - **Rails-Style Structure + Module Index** 247 | - **React Hooks** `useState`, `useEffect` 248 | - **React Testing Library** 249 | - **React** 250 | 251 | ## Requisitos: 252 | * **[Node v13.8.0](https://nodejs.org/en/)** - ou superior, instalado em seu computador. 253 | * **[Create React App](https://github.com/facebook/create-react-app)** 254 | -------------------------------------------------------------------------------- /docs/instadev.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/docs/instadev.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "instadev", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.3.2", 8 | "@testing-library/user-event": "^7.1.2", 9 | "history": "^4.10.1", 10 | "node-sass": "^4.13.1", 11 | "prop-types": "^15.7.2", 12 | "react": "^16.13.1", 13 | "react-dom": "^16.13.1", 14 | "react-router-dom": "^5.1.2", 15 | "react-scripts": "3.4.1" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": "react-app" 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/favicon.png -------------------------------------------------------------------------------- /public/fontawesome.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome Free 5.12.1 by @fontawesome - https://fontawesome.com 3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) 4 | */ 5 | .fa,.fab,.fad,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adobe:before{content:"\f778"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-bahai:before{content:"\f666"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buy-n-large:before{content:"\f8a6"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caravan:before{content:"\f8ff"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-alt:before{content:"\f422"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-cotton-bureau:before{content:"\f89e"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dailymotion:before{content:"\f952"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-alt:before{content:"\f424"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-firefox-browser:before{content:"\f907"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-handshake:before{content:"\f2b5"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-hat-wizard:before{content:"\f6e8"}.fa-hdd:before{content:"\f0a0"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-ideal:before{content:"\f913"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-instagram-square:before{content:"\f955"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-mdb:before{content:"\f8ca"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microblog:before{content:"\f91a"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mixer:before{content:"\f956"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse:before{content:"\f8cc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-orcid:before{content:"\f8d2"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-square:before{content:"\f91e"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-record-vinyl:before{content:"\f8d9"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopify:before{content:"\f957"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swift:before{content:"\f8e1"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-trailer:before{content:"\f941"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbraco:before{content:"\f8e8"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-unity:before{content:"\f949"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:400;font-display:auto;src:url(webfonts/fa-brands-400.eot);src:url(webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(webfonts/fa-brands-400.woff2) format("woff2"),url(webfonts/fa-brands-400.woff) format("woff"),url(webfonts/fa-brands-400.ttf) format("truetype"),url(webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands"}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:auto;src:url(webfonts/fa-regular-400.eot);src:url(webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(webfonts/fa-regular-400.woff2) format("woff2"),url(webfonts/fa-regular-400.woff) format("woff"),url(webfonts/fa-regular-400.ttf) format("truetype"),url(webfonts/fa-regular-400.svg#fontawesome) format("svg")}.fab,.far{font-weight:400}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:auto;src:url(webfonts/fa-solid-900.eot);src:url(webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(webfonts/fa-solid-900.woff2) format("woff2"),url(webfonts/fa-solid-900.woff) format("woff"),url(webfonts/fa-solid-900.ttf) format("truetype"),url(webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.far,.fas{font-family:"Font Awesome 5 Free"}.fa,.fas{font-weight:900} 6 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 30 | Instadev 31 | 32 | 33 | 34 |
35 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Instadev - React", 3 | "name": "Vinicius Ribeiro's Challenge", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#08111E", 24 | "background_color": "#08111E" 25 | } 26 | -------------------------------------------------------------------------------- /public/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS and IE text size adjust after device orientation change, 6 | * without disabling user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability of focused elements when they are also in an 95 | * active/hover state. 96 | */ 97 | 98 | a:active, 99 | a:hover { 100 | outline: 0; 101 | } 102 | 103 | /* Text-level semantics 104 | ========================================================================== */ 105 | 106 | /** 107 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 108 | */ 109 | 110 | abbr[title] { 111 | border-bottom: 1px dotted; 112 | } 113 | 114 | /** 115 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 116 | */ 117 | 118 | b, 119 | strong { 120 | font-weight: bold; 121 | } 122 | 123 | /** 124 | * Address styling not present in Safari and Chrome. 125 | */ 126 | 127 | dfn { 128 | font-style: italic; 129 | } 130 | 131 | /** 132 | * Address variable `h1` font-size and margin within `section` and `article` 133 | * contexts in Firefox 4+, Safari, and Chrome. 134 | */ 135 | 136 | h1 { 137 | font-size: 2em; 138 | margin: 0.67em 0; 139 | } 140 | 141 | /** 142 | * Address styling not present in IE 8/9. 143 | */ 144 | 145 | mark { 146 | background: #ff0; 147 | color: #000; 148 | } 149 | 150 | /** 151 | * Address inconsistent and variable font size in all browsers. 152 | */ 153 | 154 | small { 155 | font-size: 80%; 156 | } 157 | 158 | /** 159 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 160 | */ 161 | 162 | sub, 163 | sup { 164 | font-size: 75%; 165 | line-height: 0; 166 | position: relative; 167 | vertical-align: baseline; 168 | } 169 | 170 | sup { 171 | top: -0.5em; 172 | } 173 | 174 | sub { 175 | bottom: -0.25em; 176 | } 177 | 178 | /* Embedded content 179 | ========================================================================== */ 180 | 181 | /** 182 | * Remove border when inside `a` element in IE 8/9/10. 183 | */ 184 | 185 | img { 186 | border: 0; 187 | } 188 | 189 | /** 190 | * Correct overflow not hidden in IE 9/10/11. 191 | */ 192 | 193 | svg:not(:root) { 194 | overflow: hidden; 195 | } 196 | 197 | /* Grouping content 198 | ========================================================================== */ 199 | 200 | /** 201 | * Address margin not present in IE 8/9 and Safari. 202 | */ 203 | 204 | figure { 205 | margin: 1em 40px; 206 | } 207 | 208 | /** 209 | * Address differences between Firefox and other browsers. 210 | */ 211 | 212 | hr { 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome. 354 | */ 355 | 356 | input[type="search"] { 357 | -webkit-appearance: textfield; /* 1 */ 358 | box-sizing: content-box; /* 2 */ 359 | } 360 | 361 | /** 362 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 363 | * Safari (but not Chrome) clips the cancel button when the search input has 364 | * padding (and `textfield` appearance). 365 | */ 366 | 367 | input[type="search"]::-webkit-search-cancel-button, 368 | input[type="search"]::-webkit-search-decoration { 369 | -webkit-appearance: none; 370 | } 371 | 372 | /** 373 | * Define consistent border, margin, and padding. 374 | */ 375 | 376 | fieldset { 377 | border: 1px solid #c0c0c0; 378 | margin: 0 2px; 379 | padding: 0.35em 0.625em 0.75em; 380 | } 381 | 382 | /** 383 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 384 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 385 | */ 386 | 387 | legend { 388 | border: 0; /* 1 */ 389 | padding: 0; /* 2 */ 390 | } 391 | 392 | /** 393 | * Remove default vertical scrollbar in IE 8/9/10/11. 394 | */ 395 | 396 | textarea { 397 | overflow: auto; 398 | } 399 | 400 | /** 401 | * Don't inherit the `font-weight` (applied by a rule above). 402 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 403 | */ 404 | 405 | optgroup { 406 | font-weight: bold; 407 | } 408 | 409 | /* Tables 410 | ========================================================================== */ 411 | 412 | /** 413 | * Remove most spacing between table cells. 414 | */ 415 | 416 | table { 417 | border-collapse: collapse; 418 | border-spacing: 0; 419 | } 420 | 421 | td, 422 | th { 423 | padding: 0; 424 | } 425 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /public/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /public/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /public/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/public/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /src/__tests__/mocks/comments.json: -------------------------------------------------------------------------------- 1 | [{"id":"1","postId":"1","createdAt":"2020-03-27T00:58:06.401Z","comment":"programming the transmitter won't do anything, we need to override the back-end PNG bus!","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/yayteejay/128.jpg","name":"Santino Rowe","meta":{"id":"1","userId":"1","imageUrl":"https://viniciusvinna.netlify.app/assets//api-instagram/profiles/black-panther/black-panther-1.jpg"}},{"id":"31","postId":"1","createdAt":"2020-03-27T04:04:36.486Z","comment":"I'll navigate the digital SCSI matrix, that should protocol the SAS pixel!","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/kosmar/128.jpg","name":"Virginia Kshlerin","meta":{"id":"1","userId":"1","imageUrl":"https://viniciusvinna.netlify.app/assets//api-instagram/profiles/black-panther/black-panther-1.jpg"}}] -------------------------------------------------------------------------------- /src/__tests__/mocks/likes.json: -------------------------------------------------------------------------------- 1 | [{"id":"1","postId":"1","meta":{"id":"1","userId":"1","imageUrl":"https://viniciusvinna.netlify.app/assets//api-instagram/profiles/black-panther/black-panther-1.jpg"}}] -------------------------------------------------------------------------------- /src/__tests__/mocks/posts.json: -------------------------------------------------------------------------------- 1 | [{"id":"1","userId":"1","imageUrl":"https://viniciusvinna.netlify.app/assets//api-instagram/profiles/black-panther/black-panther-1.jpg","comments":[{"id":"1","postId":"1","createdAt":"2020-03-27T00:58:06.401Z","comment":"programming the transmitter won't do anything, we need to override the back-end PNG bus!","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/yayteejay/128.jpg","name":"Santino Rowe"},{"id":"31","postId":"1","createdAt":"2020-03-27T04:04:36.486Z","comment":"I'll navigate the digital SCSI matrix, that should protocol the SAS pixel!","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/kosmar/128.jpg","name":"Virginia Kshlerin"}],"likes":[{"id":"1","postId":"1"}]},{"id":"7","userId":"1","imageUrl":"https://viniciusvinna.netlify.app/assets//api-instagram/profiles/black-panther/black-panther-2.jpg","comments":[{"id":"7","postId":"7","createdAt":"2020-03-26T22:13:09.348Z","comment":"Use the digital XML firewall, then you can hack the bluetooth card!","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/adamawesomeface/128.jpg","name":"Delfina Bahringer"},{"id":"37","postId":"7","createdAt":"2020-03-26T23:54:44.131Z","comment":"Try to compress the COM capacitor, maybe it will bypass the wireless feed!","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/ultragex/128.jpg","name":"Felton Crooks"}],"likes":[{"id":"7","postId":"7"}]},{"id":"13","userId":"1","imageUrl":"https://viniciusvinna.netlify.app/assets//api-instagram/profiles/black-panther/black-panther-3.jpg","comments":[{"id":"13","postId":"13","createdAt":"2020-03-26T08:16:49.988Z","comment":"You can't copy the program without parsing the wireless JSON feed!","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/toddrew/128.jpg","name":"Damien Hodkiewicz"},{"id":"43","postId":"13","createdAt":"2020-03-26T14:00:15.130Z","comment":"Use the neural PNG matrix, then you can quantify the haptic protocol!","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/j2deme/128.jpg","name":"Theodora Turner"}],"likes":[{"id":"13","postId":"13"}]},{"id":"19","userId":"1","imageUrl":"https://viniciusvinna.netlify.app/assets//api-instagram/profiles/black-panther/black-panther-4.jpg","comments":[{"id":"19","postId":"19","createdAt":"2020-03-26T09:02:31.626Z","comment":"The PNG bandwidth is down, transmit the digital pixel so we can synthesize the JSON pixel!","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/baliomega/128.jpg","name":"Delbert Schneider"},{"id":"49","postId":"19","createdAt":"2020-03-26T15:33:40.603Z","comment":"Use the bluetooth THX interface, then you can copy the online system!","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/mr_shiznit/128.jpg","name":"Anastasia Waters"}],"likes":[{"id":"19","postId":"19"}]},{"id":"25","userId":"1","imageUrl":"https://viniciusvinna.netlify.app/assets//api-instagram/profiles/black-panther/black-panther-5.jpg","comments":[{"id":"25","postId":"25","createdAt":"2020-03-27T00:37:32.914Z","comment":"I'll navigate the back-end GB monitor, that should port the CSS system!","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/jagan123/128.jpg","name":"Bonnie Rosenbaum"},{"id":"55","postId":"25","createdAt":"2020-03-27T00:08:25.004Z","comment":"Try to input the EXE application, maybe it will compress the wireless program!","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/de_ascanio/128.jpg","name":"Marisol Thompson"}],"likes":[{"id":"25","postId":"25"}]}] -------------------------------------------------------------------------------- /src/__tests__/mocks/stories.json: -------------------------------------------------------------------------------- 1 | [{"id":"1","userId":"1","videoUrl":"https://viniciusvinna.netlify.app/assets/api-instagram/profiles/black-panther/black-panther-stories.mp4"},{"id":"2","userId":"2","videoUrl":"https://viniciusvinna.netlify.app/assets/api-instagram/profiles/bruce/bruce-stories.mp4"},{"id":"3","userId":"5","videoUrl":"https://viniciusvinna.netlify.app/assets/api-instagram/profiles/gamora/gamora-stories.mp4"}] -------------------------------------------------------------------------------- /src/__tests__/mocks/users.json: -------------------------------------------------------------------------------- 1 | [{"id":"1","name":"T'Challa","avatar":"https://viniciusvinna.netlify.app/assets//api-instagram/profiles/black-panther/black-panther-profile.jpg","username":"blackpanther","email":"blackpanther@gmail.com"},{"id":"2","name":"Bruce Wayne","avatar":"https://viniciusvinna.netlify.app/assets//api-instagram/profiles/bruce/bruce-profile.jpg","username":"brucewayne","email":"iamnotthebatman@hotmail.com"},{"id":"3","name":"Carol Danvers","avatar":"https://viniciusvinna.netlify.app/assets//api-instagram/profiles/carol/carol-profile.jpg","username":"captainmarvel","email":"captainmarvel@yahoo.com"},{"id":"4","name":"Domino","avatar":"https://viniciusvinna.netlify.app/assets//api-instagram/profiles/domino/domino-profile.jpg","username":"domino","email":"domino@hotmail.com"},{"id":"5","name":"Gamora","avatar":"https://viniciusvinna.netlify.app/assets//api-instagram/profiles/gamora/gamora-profile.jpg","username":"gamora","email":"Abigayle.Kemmer32@yahoo.com"},{"id":"6","name":"Mestre Yoda","avatar":"https://viniciusvinna.netlify.app/assets//api-instagram/profiles/yoda/yoda-profile.jpg","username":"mestrejedi","email":"yoda@gmail.com"},{"id":"7","name":"Vinna","avatar":"https://media-exp1.licdn.com/dms/image/C5603AQHTgizSqSTETg/profile-displayphoto-shrink_200_200/0?e=1591228800&v=beta&t=TFz7U7fVtPsbhPwyMti7vxa2EV-MagPZSPB10fVMP00","username":"thevinnacomedia","email":"vinna@hotmail.com"},{"id":"8","name":"Hedênica Morais","avatar":"https://media-exp1.licdn.com/dms/image/C4D03AQEbY-NsBLjfDQ/profile-displayphoto-shrink_200_200/0?e=1591228800&v=beta&t=qbrMFFRTaa4hCJtajlTHHQzQyqQ9OdP5eleR0TvnWcY","username":"denny","email":"hedenica@hotmail.com"},{"id":"9","name":"Marina Tujimura","avatar":"https://media-exp1.licdn.com/dms/image/C4E03AQF4m60UnP3l_A/profile-displayphoto-shrink_200_200/0?e=1591228800&v=beta&t=Z0sB-fo-g02YAG_IsIjLWjwsoxI1UI_tPdNpBQvBhn0","username":"tuji","email":"mtujimura@gmail.com"},{"id":"10","name":"Paloma Correa","avatar":"https://media-exp1.licdn.com/dms/image/C4E03AQGEOX3z7y-_Lw/profile-displayphoto-shrink_200_200/0?e=1591228800&v=beta&t=Iuvpl_-StJrxtGhhPRic1HPmIsKchjmRpE66EUO7QJ8","username":"palomacorrea","email":"paloma@email.com"},{"id":"11","name":"thierry rene matos","avatar":"https://media-exp1.licdn.com/dms/image/C4D03AQFAf-5eABXhLg/profile-displayphoto-shrink_100_100/0?e=1591228800&v=beta&t=14sgXfCa8nRu12qmeSX8pCr--Vflw4DDFEZ6RTXGW0M","username":"thierryrene","email":"teste@teste.com"},{"id":"12","name":"Chicão","avatar":"https://media-exp1.licdn.com/dms/image/C4D03AQGSnLxpcjS2MQ/profile-displayphoto-shrink_200_200/0?e=1591228800&v=beta&t=-oxSRBcevGLa7o3W0H-1_CLbYKXIkAwbGnZ4VZ14oP0","username":"iamchicao","email":"jr-francis2011@hotmail.com"},{"id":"14","name":"Raissa Barata","avatar":"https://media-exp1.licdn.com/dms/image/C4E03AQEiAT18LS-6iA/profile-displayphoto-shrink_200_200/0?e=1591228800&v=beta&t=iGTl5Q5p9yhSsj4ZIdGSe0midvFk83h8imPbxGqK5gU","username":"raissamariab","email":"raissamaria.sb@gmail.com"},{"id":"16","name":"Mariana Zangrossi","avatar":"https://media-exp1.licdn.com/dms/image/C4D03AQFzQ4ZinuaZZA/profile-displayphoto-shrink_200_200/0?e=1591833600&v=beta&t=lHCTNprrsgTJSM7aPeBa9RTPGYFkLVZjICt9IM13b84","username":"marianazangrossi","email":"mari.zangue@gmail.com"},{"id":"17","name":"Nicoli","avatar":"https://media-exp1.licdn.com/dms/image/C4E03AQHt1a1J-QyBLg/profile-displayphoto-shrink_200_200/0?e=1591833600&v=beta&t=FfhCnWZebxjQdCOtUU0F0gMHbZoe_3oLu-X_W1j8lP0","username":"nicolifelix","email":""},{"id":"18","name":"Dara","avatar":"https://media-exp1.licdn.com/dms/image/C4D03AQFoITQkJLXC0w/profile-displayphoto-shrink_200_200/0?e=1591833600&v=beta&t=dj70fKZUW8lBMNg1o8B3OLmOYxhFLTdNQaOoJdgbLtw","username":"arad","email":"daraamedeiros@outlook.com"},{"id":"20","name":"mariana","avatar":"https://media-exp1.licdn.com/dms/image/C4D03AQFzQ4ZinuaZZA/profile-displayphoto-shrink_200_200/0?e=1591833600&v=beta&t=lHCTNprrsgTJSM7aPeBa9RTPGYFkLVZjICt9IM13b84","username":"mariana","email":"mariana@gmail.com"},{"id":"21","name":"John Doe","avatar":"","username":"johndoe","email":"johndoe@gmail.com"}] -------------------------------------------------------------------------------- /src/__tests__/validation.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import '@testing-library/jest-dom/extend-expect'; 3 | import { createMemoryHistory } from 'history'; 4 | import { Router } from 'react-router-dom'; 5 | 6 | import { render } from '@testing-library/react'; 7 | 8 | import Loading from '../components/Loading'; 9 | import Post from '../components/Post'; 10 | import Story from '../components/Story'; 11 | import SuccessMessage from '../components/SuccessMessage'; 12 | import Topbar from '../components/Topbar'; 13 | import User from '../components/User'; 14 | 15 | import App from '../containers/App'; 16 | import Posts from '../containers/Posts'; 17 | import Stories from '../containers/Stories'; 18 | import UserForm from '../containers/UserForm'; 19 | import UserPosts from '../containers/UserPosts'; 20 | import UserProfile from '../containers/UserProfile'; 21 | 22 | import FeedRoute from '../routes/FeedRoute'; 23 | import NewUserRoute from '../routes/NewUserRoute'; 24 | import ProfileRoute from '../routes/ProfileRoute'; 25 | import UsersRoute from '../routes/UsersRoute'; 26 | 27 | const history = createMemoryHistory(); 28 | 29 | describe('React Instagram APP', () => { 30 | describe('Folder Strucutre and data-test-id attributes', () => { 31 | it('should should render properly the component', () => { 32 | const { getByTestId } = render(); 33 | const container = getByTestId('loading'); 34 | 35 | expect(container).toBeDefined(); 36 | }); 37 | 38 | it('should should render properly the component', () => { 39 | const propsMock = { 40 | postInfo: { comments: [] }, 41 | userInfo: {}, 42 | }; 43 | 44 | const { getByTestId } = render( 45 | 46 | 47 | 48 | ); 49 | const container = getByTestId('post'); 50 | 51 | expect(container).toBeDefined(); 52 | }); 53 | 54 | it('should should render properly the component', () => { 55 | const propsMock = { 56 | story: {}, 57 | user: { username: 'Vinna'}, 58 | handleClose: {} 59 | 60 | }; 61 | const { getByTestId } = render( 62 | 63 | 64 | 65 | ); 66 | const container = getByTestId('story'); 67 | 68 | expect(container).toBeDefined(); 69 | }); 70 | 71 | it('should should render properly the component', () => { 72 | const { getByTestId } = render( 73 | 74 | 75 | 76 | ); 77 | const container = getByTestId('success-message'); 78 | 79 | expect(container).toBeDefined(); 80 | }); 81 | 82 | it('should should render properly the component', () => { 83 | const { getByTestId } = render( 84 | 85 | 86 | 87 | ); 88 | const container = getByTestId('topbar'); 89 | 90 | expect(container).toBeDefined(); 91 | }); 92 | 93 | it('should should render properly the component', () => { 94 | const propsMock = { 95 | infoUser: { 96 | avatar: '', 97 | name: '', 98 | username: '', 99 | } 100 | }; 101 | 102 | const { getByTestId } = render( 103 | 104 | 105 | 106 | ); 107 | const container = getByTestId('user'); 108 | 109 | expect(container).toBeDefined(); 110 | }); 111 | 112 | it('should should render properly the container', () => { 113 | const { getByTestId } = render( 114 | 115 | 116 | 117 | ); 118 | const container = getByTestId('app'); 119 | 120 | expect(container).toBeDefined(); 121 | }); 122 | 123 | it('should should render properly the container', () => { 124 | const propsMock = { 125 | posts: [], 126 | }; 127 | 128 | const { getByTestId } = render( 129 | 130 | 131 | 132 | ); 133 | const container = getByTestId('posts'); 134 | 135 | expect(container).toBeDefined(); 136 | }); 137 | 138 | it('should should render properly the container', () => { 139 | const propsMock = { 140 | stories: [], 141 | }; 142 | 143 | const { getByTestId } = render( 144 | 145 | 146 | 147 | ); 148 | 149 | const container = getByTestId('stories'); 150 | 151 | expect(container).toBeDefined(); 152 | }); 153 | 154 | it('should should render properly the container', () => { 155 | const { getByTestId } = render( 156 | 157 | 158 | 159 | ); 160 | const container = getByTestId('user-form'); 161 | 162 | expect(container).toBeDefined(); 163 | }); 164 | 165 | it('should should render properly the container', () => { 166 | const propsMock = { 167 | posts: [], 168 | }; 169 | 170 | const { getByTestId } = render( 171 | 172 | 173 | 174 | ); 175 | const container = getByTestId('user-posts'); 176 | 177 | expect(container).toBeDefined(); 178 | }); 179 | 180 | it('should should render properly the container', () => { 181 | const propsMock = { 182 | avatar: '', 183 | name: '', 184 | username: '', 185 | }; 186 | 187 | const { getByTestId } = render( 188 | 189 | 190 | 191 | ); 192 | const container = getByTestId('user-profile'); 193 | 194 | expect(container).toBeDefined(); 195 | }); 196 | }); 197 | 198 | it('should should render properly the component', () => { 199 | const { getByTestId } = render( 200 | 201 | 202 | 203 | ); 204 | const container = getByTestId('feed-route'); 205 | 206 | expect(container).toBeDefined(); 207 | }); 208 | 209 | it('should should render properly the component', () => { 210 | const { getByTestId } = render( 211 | 212 | 213 | 214 | ); 215 | const container = getByTestId('new-user-route'); 216 | 217 | expect(container).toBeDefined(); 218 | }); 219 | 220 | it('should should render properly the component', () => { 221 | const { getByTestId } = render( 222 | 223 | 224 | 225 | ); 226 | 227 | const container = getByTestId('profile-route'); 228 | 229 | expect(container).toBeDefined(); 230 | }); 231 | 232 | it('should should render properly the component', () => { 233 | const { getByTestId } = render( 234 | 235 | 236 | 237 | ); 238 | const container = getByTestId('users-route'); 239 | 240 | expect(container).toBeDefined(); 241 | }); 242 | }); 243 | 244 | -------------------------------------------------------------------------------- /src/assets/img/instagram-glyph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/src/assets/img/instagram-glyph.png -------------------------------------------------------------------------------- /src/assets/img/instagram-logo.svg: -------------------------------------------------------------------------------- 1 | instagram-loco 2 | -------------------------------------------------------------------------------- /src/components/Loading/Loading.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './Loading.scss'; 4 | 5 | const Loading = () => ( 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | Carregando 17 |
18 | ); 19 | 20 | export default Loading; 21 | -------------------------------------------------------------------------------- /src/components/Loading/Loading.scss: -------------------------------------------------------------------------------- 1 | .loading { 2 | align-items: center; 3 | color: #16202B; 4 | display: flex; 5 | flex-direction: column; 6 | height: 100px; 7 | justify-content: center; 8 | left: 50%; 9 | position: absolute; 10 | top: 50%; 11 | transform: translate(-50%); 12 | width: 100px; 13 | font-weight: bold; 14 | font-size: 1.3rem; 15 | } 16 | 17 | .preloader { 18 | position: relative; 19 | width: 65px; 20 | margin-bottom: 20px; 21 | } 22 | 23 | .preloader span { 24 | position: absolute; 25 | display: block; 26 | bottom: 0; 27 | width: 9px; 28 | height: 5px; 29 | border-radius: 5px; 30 | background: #16202B; 31 | animation: preloader 2s infinite ease-in-out; 32 | } 33 | 34 | .preloader span:nth-child(2) { 35 | left: 11px; 36 | -webkit-animation-delay: 200ms; 37 | animation-delay: 200ms; 38 | } 39 | 40 | .preloader span:nth-child(3) { 41 | left: 22px; 42 | -webkit-animation-delay: 400ms; 43 | animation-delay: 400ms; 44 | } 45 | 46 | .preloader span:nth-child(4) { 47 | left: 33px; 48 | -webkit-animation-delay: 600ms; 49 | animation-delay: 600ms; 50 | } 51 | 52 | .preloader span:nth-child(5) { 53 | left: 44px; 54 | -webkit-animation-delay: 800ms; 55 | animation-delay: 800ms; 56 | } 57 | 58 | .preloader span:nth-child(6) { 59 | left: 55px; 60 | -webkit-animation-delay: 1000ms; 61 | animation-delay: 1000ms; 62 | } 63 | 64 | @keyframes preloader { 65 | 0% { 66 | height: 5px; 67 | -webkit-transform: translateY(0); 68 | transform: translateY(0); 69 | background: #f88738; 70 | } 71 | 25% { 72 | height: 30px; 73 | -webkit-transform: translateY(15px); 74 | transform: translateY(15px); 75 | background: #DC327B; 76 | } 77 | 50%,100% { 78 | height: 5px; 79 | -webkit-transform: translateY(0); 80 | transform: translateY(0); 81 | background: #16202B; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/components/Loading/index.jsx: -------------------------------------------------------------------------------- 1 | import Loading from './Loading'; 2 | 3 | export default Loading; 4 | -------------------------------------------------------------------------------- /src/components/Post/Post.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | import './Post.scss'; 5 | 6 | const Post = ({ postInfo, userInfo }) => { 7 | const [like, setLike] = useState(false); 8 | const [follow, toggleFollow] = useState(false); 9 | 10 | const { comments, imageUrl } = postInfo; 11 | 12 | return ( 13 |
14 | {userInfo && ( 15 |
16 |
17 | 18 | {userInfo.name} 19 | 20 | 21 | {userInfo.name} 22 |
23 | 24 | 33 |
34 | )} 35 | 36 |
37 | 38 |
39 | 40 | {userInfo && ( 41 | 63 | )} 64 |
65 | ); 66 | }; 67 | 68 | export default Post; 69 | -------------------------------------------------------------------------------- /src/components/Post/Post.scss: -------------------------------------------------------------------------------- 1 | .post { 2 | background-color: #08111E; 3 | display: flex; 4 | flex-direction: column; 5 | margin-bottom: 30px; 6 | overflow: hidden; 7 | } 8 | 9 | .post__header { 10 | align-items: center; 11 | display: flex; 12 | justify-content: space-between; 13 | padding: 8px; 14 | } 15 | 16 | .post__header .user { 17 | display: flex; 18 | align-items: center; 19 | } 20 | 21 | .post__header .user__thumb { 22 | height: 40px; 23 | margin-right: 8px; 24 | border: 1px solid #FF991C; 25 | width: 40px; 26 | } 27 | 28 | .follow-btn { 29 | color: #858D93; 30 | font-weight: 600; 31 | font-size: 1.2rem; 32 | 33 | &.is-following { 34 | color: #ED2168; 35 | } 36 | } 37 | 38 | .post__header .user__name { 39 | font-size: 1.2rem; 40 | font-weight: bold; 41 | color: white; 42 | text-decoration: none; 43 | } 44 | 45 | .post__context { 46 | font-size: 1.4rem; 47 | } 48 | 49 | .post__figure { 50 | border-radius: 30px; 51 | height: auto; 52 | margin: 0; 53 | overflow: hidden; 54 | width: 100%; 55 | } 56 | 57 | .post__figure img { 58 | width: 100%; 59 | } 60 | 61 | .post__controls { 62 | width: 100%; 63 | display: flex; 64 | justify-content: flex-start; 65 | align-items: center; 66 | padding: 4px 0; 67 | } 68 | 69 | .post__control { 70 | color: #858D93; 71 | font-size: 2.4rem; 72 | margin-right: 5px; 73 | position: relative; 74 | } 75 | 76 | .fas.fa-heart { 77 | color: #ED2168; 78 | } 79 | 80 | .post__status { 81 | display: flex; 82 | padding: 0; 83 | line-height: 1.4em; 84 | 85 | span { 86 | color: #858D93; 87 | } 88 | } 89 | 90 | .post__status .user { 91 | display: flex; 92 | justify-content: flex-start; 93 | align-items: center; 94 | } 95 | 96 | .post__status .user span { 97 | font-size: 1.2rem; 98 | } 99 | 100 | .post__status .user__thumb { 101 | height: 30px; 102 | width: 30px; 103 | margin-right: 8px; 104 | } 105 | -------------------------------------------------------------------------------- /src/components/Post/index.jsx: -------------------------------------------------------------------------------- 1 | import Post from './Post'; 2 | 3 | export default Post; 4 | -------------------------------------------------------------------------------- /src/components/Story/Story.jsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useState } from "react"; 2 | 3 | import { Link } from 'react-router-dom'; 4 | 5 | import './Story.scss'; 6 | 7 | const Story = ({ story, user, handleClose }) => { 8 | const [metadata, setMetadata] = useState(null); 9 | const [currentTime, setCurrentTime] = useState(null); 10 | 11 | const updateProgress = useCallback( 12 | () => { 13 | if (metadata?.duration !== null && currentTime !== null) { 14 | const elapsedTime = ((currentTime / metadata.duration) * 100); 15 | 16 | return `${elapsedTime.toFixed(2)}%`; 17 | } 18 | 19 | return '0%'; 20 | }, [metadata, currentTime]); 21 | 22 | return ( 23 |
24 |
25 |
26 |
27 | 28 | {user.name} 29 | 30 | 31 | {user.name} 32 |
33 | 34 | 40 |
41 | 42 |
43 |
47 |
48 |
49 | 50 | {story.videoUrl && ( 51 |
52 |
53 |
69 |
70 | )} 71 |
72 | ); 73 | }; 74 | 75 | export default Story; 76 | -------------------------------------------------------------------------------- /src/components/Story/Story.scss: -------------------------------------------------------------------------------- 1 | .story { 2 | background-color: #08111E; 3 | display: flex; 4 | flex-direction: column; 5 | height: 100vh; 6 | left: 0; 7 | position: fixed; 8 | top: 0; 9 | width: 100%; 10 | z-index: 9999; 11 | } 12 | 13 | .story__header { 14 | overflow: hidden; 15 | padding: 16px; 16 | align-items: center; 17 | display: flex; 18 | justify-content: space-between; 19 | } 20 | 21 | .story__progress { 22 | background-color: rgba(255, 255, 255, .05); 23 | border-radius: 4px; 24 | height: 4px; 25 | overflow: hidden; 26 | width: 100%; 27 | padding: 1px; 28 | } 29 | 30 | .story__progress__elapsed { 31 | transition: width ease-in-out 0.05s; 32 | width: 0; 33 | height: 1px; 34 | background: linear-gradient(to right, #FF991C 0%, #D51589 100%); 35 | } 36 | 37 | .story__close { 38 | font-size: 1.6rem; 39 | color: #858d93; 40 | } 41 | 42 | .story__header .user { 43 | display: flex; 44 | align-items: center; 45 | } 46 | 47 | .story__header .user__thumb { 48 | height: 40px; 49 | margin-right: 8px; 50 | border: 1px solid #FF991C; 51 | width: 40px; 52 | } 53 | 54 | .story__header .user__name { 55 | font-size: 1.2rem; 56 | font-weight: bold; 57 | color: white; 58 | text-decoration: none; 59 | } 60 | 61 | .story__video__wrapper { 62 | background-color: black; 63 | height: calc(100vh - 104px); 64 | overflow: hidden; 65 | border: 5px solid #16202b; 66 | padding: 0; 67 | border-radius: 30px; 68 | position: relative; 69 | width: 100%; 70 | margin: 16px 0; 71 | }; 72 | 73 | .video-player { 74 | position: absolute; 75 | width: 100%; 76 | max-width: 100%; 77 | height: 100%; 78 | } 79 | -------------------------------------------------------------------------------- /src/components/Story/index.jsx: -------------------------------------------------------------------------------- 1 | import Story from './Story'; 2 | 3 | export default Story; 4 | -------------------------------------------------------------------------------- /src/components/SuccessMessage/SuccessMessage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Link } from 'react-router-dom'; 4 | 5 | import './SuccessMessage.scss'; 6 | 7 | const SuccessMessage = () => ( 8 |
9 | 16 | 21 | 31 | 32 | 39 | 40 | 41 |

Usuário Cadastrado!

42 | 43 |
44 |

Visite a página users para visualizar o novo usuário

45 |
46 |
47 | ); 48 | 49 | export default SuccessMessage; 50 | -------------------------------------------------------------------------------- /src/components/SuccessMessage/SuccessMessage.scss: -------------------------------------------------------------------------------- 1 | .success-message { 2 | align-items: center; 3 | background-color: #4caf50; 4 | border-radius: 30px; 5 | display: flex; 6 | flex-direction: column; 7 | height: 300px; 8 | justify-content: center; 9 | left: 50%; 10 | margin: auto; 11 | max-width: 400px; 12 | overflow: hidden; 13 | padding: 20px; 14 | position: fixed; 15 | text-align: center; 16 | top: 50%; 17 | transform: translate(-50%,-50%); 18 | width: 80%; 19 | z-index: 2; 20 | 21 | &__title { 22 | font-size: 2.4rem; 23 | line-height: 1; 24 | } 25 | 26 | &__content { 27 | font-size: 1.4rem; 28 | line-height: 1; 29 | 30 | a { 31 | color: #08111e; 32 | text-decoration: underline; 33 | } 34 | } 35 | } 36 | 37 | .successAnimationCircle { 38 | stroke-dasharray: 151px; 39 | stroke: #FFF; 40 | } 41 | 42 | .successAnimationCheck { 43 | stroke-dasharray: 36px 36px; 44 | stroke: #FFF; 45 | } 46 | 47 | .successAnimationResult { 48 | fill: #FFF; 49 | opacity: 0; 50 | } 51 | 52 | .successAnimation.animated { 53 | -webkit-animation: 1s ease-out 0s 1 both scaleAnimation; 54 | animation: 1s ease-out 0s 1 both scaleAnimation; 55 | } 56 | .successAnimation.animated .successAnimationCircle { 57 | -webkit-animation: 1s cubic-bezier(0.77, 0, 0.175, 1) 0s 1 both drawCircle, 0.3s linear 0.9s 1 both fadeOut; 58 | animation: 1s cubic-bezier(0.77, 0, 0.175, 1) 0s 1 both drawCircle, 0.3s linear 0.9s 1 both fadeOut; 59 | } 60 | .successAnimation.animated .successAnimationCheck { 61 | -webkit-animation: 1s cubic-bezier(0.77, 0, 0.175, 1) 0s 1 both drawCheck, 0.3s linear 0.9s 1 both fadeOut; 62 | animation: 1s cubic-bezier(0.77, 0, 0.175, 1) 0s 1 both drawCheck, 0.3s linear 0.9s 1 both fadeOut; 63 | } 64 | .successAnimation.animated .successAnimationResult { 65 | -webkit-animation: 0.3s linear 0.9s both fadeIn; 66 | animation: 0.3s linear 0.9s both fadeIn; 67 | } 68 | 69 | @keyframes scaleAnimation { 70 | 0% { 71 | opacity: 0; 72 | -webkit-transform: scale(1.5); 73 | transform: scale(1.5); 74 | } 75 | 100% { 76 | opacity: 1; 77 | -webkit-transform: scale(1); 78 | transform: scale(1); 79 | } 80 | } 81 | 82 | @keyframes drawCircle { 83 | 0% { 84 | stroke-dashoffset: 151px; 85 | } 86 | 100% { 87 | stroke-dashoffset: 0; 88 | } 89 | } 90 | 91 | @keyframes drawCheck { 92 | 0% { 93 | stroke-dashoffset: 36px; 94 | } 95 | 100% { 96 | stroke-dashoffset: 0; 97 | } 98 | } 99 | 100 | @keyframes fadeOut { 101 | 0% { 102 | opacity: 1; 103 | } 104 | 100% { 105 | opacity: 0; 106 | } 107 | } 108 | 109 | @keyframes fadeIn { 110 | 0% { 111 | opacity: 0; 112 | } 113 | 100% { 114 | opacity: 1; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/components/SuccessMessage/index.jsx: -------------------------------------------------------------------------------- 1 | import SuccessMessage from './SuccessMessage'; 2 | 3 | export default SuccessMessage; 4 | -------------------------------------------------------------------------------- /src/components/Topbar/Topbar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Link } from 'react-router-dom'; 4 | 5 | import { ReactComponent as LogoSvg } from '../../assets/img/instagram-logo.svg'; 6 | 7 | import './Topbar.scss'; 8 | 9 | const Topbar = () => ( 10 |
11 |
12 | 13 | 14 | 15 | 16 |
17 | 23 | 24 | 30 |
31 |
32 |
33 | ); 34 | 35 | export default Topbar; 36 | -------------------------------------------------------------------------------- /src/components/Topbar/Topbar.scss: -------------------------------------------------------------------------------- 1 | .topbar { 2 | backface-visibility: hidden; 3 | background-color: #16202B; 4 | left: 0; 5 | padding: 8px 16px; 6 | position: fixed; 7 | top: 0; 8 | width: 100%; 9 | z-index: 9000; 10 | } 11 | 12 | .topbar .container { 13 | display: flex; 14 | justify-content: space-between; 15 | } 16 | 17 | .topbar__logo { 18 | display: inline-block; 19 | height: 40px; 20 | } 21 | 22 | .topbar__logo svg { 23 | height: 100%; 24 | } 25 | 26 | .topbar__group { 27 | display: flex; 28 | justify-content: flex-end; 29 | } 30 | 31 | .topbar__icon, 32 | .topbar__icon a { 33 | align-items: center; 34 | color: #858D93; 35 | display: flex; 36 | flex-direction: column; 37 | font-size: 1.6rem; 38 | font-weight: normal; 39 | justify-content: center; 40 | margin: auto 0.5rem; 41 | transition: all 0.2s ease-in-out; 42 | 43 | span { 44 | display: none; 45 | } 46 | } 47 | 48 | .topbar__icon { 49 | a { 50 | &:hover { 51 | color: white; 52 | } 53 | } 54 | } 55 | 56 | @media screen and (min-width: 728px) { 57 | .topbar__icon, 58 | .topbar__icon a { 59 | margin: auto 0.5rem; 60 | 61 | span { 62 | display: inline-block; 63 | font-size: 1.1rem; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/components/Topbar/index.jsx: -------------------------------------------------------------------------------- 1 | import Topbar from './Topbar'; 2 | 3 | export default Topbar; 4 | -------------------------------------------------------------------------------- /src/components/User/User.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Link } from 'react-router-dom'; 4 | 5 | const User = ({ infoUser }) => { 6 | const {avatar, name, username, } = infoUser; 7 | 8 | return ( 9 |
10 |
11 | 12 |
13 | {avatar 14 | ? {name} 15 | : 16 | } 17 |
18 | 19 |
{name}
20 | 21 |
22 |
23 | ) 24 | }; 25 | 26 | export default User; 27 | -------------------------------------------------------------------------------- /src/components/User/index.jsx: -------------------------------------------------------------------------------- 1 | import User from './User'; 2 | 3 | export default User; 4 | -------------------------------------------------------------------------------- /src/containers/App/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BrowserRouter } from 'react-router-dom'; 3 | 4 | import Topbar from '../../components/Topbar'; 5 | 6 | import Routes from '../../routes'; 7 | 8 | import './App.scss'; 9 | 10 | const App = () => ( 11 |
12 | 13 | 14 | 15 | 16 | 17 |
18 | ); 19 | 20 | export default App; 21 | -------------------------------------------------------------------------------- /src/containers/App/App.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Open+Sans:400,700&display=swap'); 2 | 3 | *, 4 | *:after, 5 | *:before { 6 | box-sizing: border-box; 7 | } 8 | 9 | html { 10 | font-size: 10px; 11 | } 12 | 13 | body { 14 | padding-top: 56px; 15 | font-family: 'Open Sans', sans-serif; 16 | background-color: #08111E; 17 | color: #fff; 18 | } 19 | 20 | a { 21 | text-decoration: none; 22 | color: #fff; 23 | font-weight: bold; 24 | } 25 | 26 | button { 27 | background-color: transparent; 28 | border: none; 29 | box-shadow: none; 30 | outline: none; 31 | -webkit-tap-highlight-color: rgba(0,0,0,0); 32 | } 33 | 34 | .container { 35 | margin: 0 auto; 36 | position: relative; 37 | width: 100%; 38 | } 39 | 40 | @media screen and (min-width: 728px) { 41 | .container { 42 | width: 600px; 43 | } 44 | } 45 | 46 | .user__thumb { 47 | display: flex; 48 | border-radius: 100%; 49 | overflow: hidden; 50 | } 51 | 52 | .user__thumb__wrapper { 53 | border-radius: 100%; 54 | border: 2px solid #16202B; 55 | display: inline-block; 56 | overflow: hidden; 57 | } 58 | 59 | .user__thumb img { 60 | height: 100%; 61 | width: 100%; 62 | } 63 | 64 | .user__thumb--hasNew { 65 | background: linear-gradient(45deg, #D51589 0%, #FF991C 100%); 66 | } 67 | -------------------------------------------------------------------------------- /src/containers/App/index.jsx: -------------------------------------------------------------------------------- 1 | import App from './App'; 2 | 3 | export default App; 4 | -------------------------------------------------------------------------------- /src/containers/Posts/Posts.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Post from '../../components/Post'; 4 | 5 | const Posts = ({ posts, getUserHandler }) => ( 6 |
7 |
8 | { posts.length > 0 && posts.map((post) => ( 9 | 14 | )) 15 | } 16 |
17 |
18 | ); 19 | 20 | export default Posts; 21 | -------------------------------------------------------------------------------- /src/containers/Posts/index.jsx: -------------------------------------------------------------------------------- 1 | import Posts from './Posts'; 2 | 3 | export default Posts; 4 | -------------------------------------------------------------------------------- /src/containers/Stories/Stories.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | import Story from '../../components/Story'; 4 | 5 | import './Stories.scss'; 6 | 7 | const Stories = ({ stories, getUserHandler }) => { 8 | const [showStory, toggleShowStory] = useState(false); 9 | const [selectedStory, setSelectedHistory] = useState({}); 10 | const [selectedProfile, setSelectedProfile] = useState({}); 11 | 12 | const findStoryById = (id) => stories.find(story => story.id === id); 13 | 14 | const handleStory = (story) => { 15 | const foundStory = findStoryById(story.id); 16 | const profileData = getUserHandler(story.userId); 17 | 18 | setSelectedProfile(profileData); 19 | setSelectedHistory(foundStory); 20 | toggleShowStory(!showStory); 21 | }; 22 | 23 | return ( 24 | 25 |
26 |
27 | {stories.map((story, index) => { 28 | const profileData = getUserHandler(story.userId); 29 | 30 | return ( 31 | 40 | )}) 41 | } 42 |
43 |
44 | 45 | {showStory && ( 46 | toggleShowStory(!showStory)} 50 | /> 51 | )} 52 |
53 | ); 54 | }; 55 | export default Stories; 56 | -------------------------------------------------------------------------------- /src/containers/Stories/Stories.scss: -------------------------------------------------------------------------------- 1 | .stories { 2 | background-color: #16202B; 3 | overflow-x: hidden; 4 | overflow-y: auto; 5 | padding: 16px; 6 | width: 100%; 7 | } 8 | 9 | .stories .container { 10 | align-items: center; 11 | display: flex; 12 | justify-content: flex-start; 13 | margin: 0 auto; 14 | } 15 | 16 | .stories .user__thumb { 17 | align-items: center; 18 | display: flex; 19 | flex-direction: column; 20 | border: none; 21 | height: 72px; 22 | justify-content: center; 23 | margin: 0 5px; 24 | padding: 0; 25 | width: 72px; 26 | } 27 | 28 | .stories .user__thumb__wrapper { 29 | height: 65px; 30 | width: 65px; 31 | } 32 | -------------------------------------------------------------------------------- /src/containers/Stories/index.jsx: -------------------------------------------------------------------------------- 1 | import Stories from './Stories'; 2 | 3 | export default Stories; 4 | -------------------------------------------------------------------------------- /src/containers/UserForm/UserForm.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | import SuccessMessage from '../../components/SuccessMessage'; 4 | 5 | import './UserForm.scss'; 6 | 7 | const UserForm = () => { 8 | const [name, setName] = useState('John Doe'); 9 | const [avatar, setAvatar] = useState(''); 10 | const [username, setUsername] = useState('johndoe'); 11 | const [email, setEmail] = useState('johndoe@gmail.com'); 12 | const [submit, setSubmit] = useState(false); 13 | 14 | const handleSetName = (event) => { 15 | const { value } = event.target; 16 | 17 | setName(value); 18 | }; 19 | 20 | const handleSetAvatar = (event) => { 21 | const { value } = event.target; 22 | 23 | setAvatar(value); 24 | }; 25 | 26 | const handleSetUserName = (event) => { 27 | const { value } = event.target; 28 | 29 | setUsername(value); 30 | }; 31 | 32 | const handleSetEmail = (event) => { 33 | const { value } = event.target; 34 | 35 | setEmail(value); 36 | }; 37 | 38 | const handleAddUser = (event) => { 39 | event.preventDefault(); 40 | 41 | const postObject = JSON.stringify({ 42 | name, 43 | avatar, 44 | username, 45 | email, 46 | }); 47 | 48 | fetch('https://5e7d0266a917d70016684219.mockapi.io/api/v1/users', { 49 | method: 'POST', 50 | headers: { 51 | 'Content-Type': 'application/json' 52 | }, 53 | body: postObject 54 | }).then(() => setSubmit(true)); 55 | }; 56 | 57 | return ( 58 | 59 |
60 |
61 |
62 |
63 |
64 | {avatar 65 | ? 66 | : 67 | } 68 |
69 | 70 | {name && ( 71 |

72 | {name} 73 | @{username} 74 |

75 | )} 76 |
77 |
78 |
79 |
80 | 81 |
82 |
83 |
84 | 85 | handleSetName(event)} 90 | /> 91 | 92 | 93 | handleSetUserName(event)} 98 | /> 99 | 100 | 101 | handleSetEmail(event)} 106 | /> 107 | 108 | 109 | handleSetAvatar(event)} 113 | /> 114 | 115 | 121 |
122 |
123 |
124 | 125 | {submit && ()} 126 |
127 | ); 128 | }; 129 | 130 | export default UserForm; 131 | -------------------------------------------------------------------------------- /src/containers/UserForm/UserForm.scss: -------------------------------------------------------------------------------- 1 | .post__form { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | padding: 16px; 6 | 7 | @media screen and (min-width: 728px) { 8 | padding: 40px; 9 | } 10 | 11 | .post__form__wrapper { 12 | width: 100%; 13 | margin: 0 auto; 14 | display: flex; 15 | flex-direction: column; 16 | justify-content: center; 17 | 18 | @media screen and (min-width: 728px) { 19 | width: 80%; 20 | } 21 | 22 | button { 23 | background: linear-gradient(45deg, #D51589 0%, #FF991C 100%); 24 | color: white; 25 | font-size: 2rem; 26 | font-weight: bold; 27 | margin: 0 auto; 28 | padding: 10px 20px; 29 | border-radius: 5px; 30 | } 31 | } 32 | 33 | label { 34 | display: block; 35 | color: #858D93; 36 | font-size: 1.2rem; 37 | font-weight: normal; 38 | margin-bottom: 5px; 39 | } 40 | 41 | input { 42 | background-color: rgba(255, 255, 255, 0.02); 43 | border-radius: 5px; 44 | border: 1px solid #858D93; 45 | color: white; 46 | display: block; 47 | font-size: 16px; 48 | font-weight: bold; 49 | margin-bottom: 20px; 50 | outline: none; 51 | padding: 8px; 52 | width: 100%; 53 | transition: all ease-in-out .2s; 54 | 55 | &::placeholder { 56 | font-weight: normal; 57 | } 58 | 59 | &:focus { 60 | background-color: rgba(255, 255, 255, 0.1); 61 | border: 1px solid #F06B41; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/containers/UserForm/index.jsx: -------------------------------------------------------------------------------- 1 | import UserForm from './UserForm'; 2 | 3 | export default UserForm; 4 | -------------------------------------------------------------------------------- /src/containers/UserPosts/UserPosts.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Post from '../../components/Post'; 4 | 5 | import './UserPosts.scss'; 6 | 7 | const UserPosts = ({ posts }) => ( 8 |
9 |
10 | { posts.length > 0 11 | ? posts.map((post) => ( 12 | 16 | )) 17 | : ( 18 |
19 | Não há publicações deste usuário 20 | 😥 21 |
22 | ) 23 | } 24 |
25 |
26 | ); 27 | 28 | export default UserPosts; 29 | -------------------------------------------------------------------------------- /src/containers/UserPosts/UserPosts.scss: -------------------------------------------------------------------------------- 1 | .user-posts { 2 | display: flex; 3 | flex-direction: row; 4 | flex-wrap: wrap; 5 | padding: 16px; 6 | position: relative; 7 | justify-content: space-between; 8 | 9 | & .post { 10 | margin: 6px; 11 | width: calc((100% / 2) - 12px); 12 | 13 | .post__figure { 14 | border-radius: 10px; 15 | } 16 | } 17 | } 18 | 19 | .no-posts { 20 | align-items: center; 21 | display: flex; 22 | flex-direction: column; 23 | font-size: 1.4rem; 24 | justify-content: center; 25 | width: 100%; 26 | padding: 40px; 27 | 28 | &__content { 29 | color: #858d93; 30 | } 31 | 32 | &__emoji { 33 | font-size: 5rem; 34 | } 35 | } 36 | 37 | @media screen and (min-width: 728px) { 38 | .user-posts { 39 | padding: 20px 16px 40px; 40 | justify-content: flex-start; 41 | 42 | & .post { 43 | width: calc((100% / 3) - 12px); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/containers/UserPosts/index.jsx: -------------------------------------------------------------------------------- 1 | import UserPosts from './UserPosts'; 2 | 3 | export default UserPosts; 4 | -------------------------------------------------------------------------------- /src/containers/UserProfile/UserProfile.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './UserProfile.scss'; 4 | 5 | const UserProfile = ({ avatar, name, username }) => { 6 | return ( 7 |
8 |
9 |
10 |
11 |
12 | { avatar.length > 0 13 | ? 14 | : 15 | } 16 |
17 | 18 | {name && ( 19 |

20 | {name} 21 | @{username} 22 |

23 | )} 24 |
25 |
26 |
27 |
28 | ) 29 | }; 30 | 31 | export default UserProfile; 32 | -------------------------------------------------------------------------------- /src/containers/UserProfile/UserProfile.scss: -------------------------------------------------------------------------------- 1 | .profile { 2 | background-color: #16202B; 3 | display: flex; 4 | flex-direction: column; 5 | position: relative; 6 | } 7 | 8 | .profile-data { 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: center; 12 | align-items: center; 13 | padding: 20px; 14 | 15 | .user { 16 | display: flex; 17 | flex-direction: column; 18 | justify-content: center; 19 | align-items: center; 20 | } 21 | 22 | .user__thumb { 23 | background: linear-gradient(45deg, #D51589 0%, #FF991C 100%); 24 | border: 2px solid transparent; 25 | height: 150px; 26 | width: 150px; 27 | margin: 0; 28 | } 29 | 30 | .user__name { 31 | display: flex; 32 | align-items: center; 33 | padding: 0; 34 | font-size: 2rem; 35 | line-height: 1; 36 | margin-bottom: 0; 37 | font-weight: bold; 38 | transition: all ease-in-out .2s; 39 | color: white; 40 | 41 | span { 42 | color: #858d93; 43 | font-weight: normal; 44 | font-size: 1.4rem; 45 | 46 | &:before { 47 | content: "-"; 48 | margin: 0 10px; 49 | } 50 | } 51 | } 52 | } 53 | 54 | @media screen and (min-width: 728px) { 55 | .profile-data { 56 | padding: 40px 16px 20px; 57 | 58 | .user__thumb { 59 | height: 200px; 60 | width: 200px; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/containers/UserProfile/index.jsx: -------------------------------------------------------------------------------- 1 | import UserProfile from './UserProfile'; 2 | 3 | export default UserProfile; 4 | -------------------------------------------------------------------------------- /src/containers/UsersList/UsersList.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import User from '../../components/User'; 4 | import Loading from '../../components/Loading'; 5 | 6 | import './UsersList.scss'; 7 | 8 | const UersList = ({ users }) => { 9 | return ( 10 |
11 | { users.length > 0 12 | ? users.map((user) => ( 13 | 17 | )) 18 | : 19 | } 20 |
21 | ) 22 | }; 23 | 24 | export default UersList; 25 | -------------------------------------------------------------------------------- /src/containers/UsersList/UsersList.scss: -------------------------------------------------------------------------------- 1 | .users-list { 2 | display: flex; 3 | flex-direction: row; 4 | flex-wrap: wrap; 5 | padding: 8px; 6 | position: relative; 7 | width: 100%; 8 | justify-content: center; 9 | align-items: center; 10 | 11 | .post { 12 | align-items: center; 13 | background-color: rgba(22, 32, 43, 1); 14 | border-radius: 6px; 15 | display: flex; 16 | flex-direction: column; 17 | justify-content: center; 18 | margin: 6px auto; 19 | overflow: hidden; 20 | padding: 0; 21 | transition: all ease-in-out .2s; 22 | width: calc((100% / 2) - 12px); 23 | 24 | &:hover { 25 | background-color: rgba(22, 32, 43, .6); 26 | 27 | 28 | .post__header .user__name { 29 | color: white; 30 | } 31 | 32 | .user__thumb { 33 | background: linear-gradient(45deg, #D51589 0%, #FF991C 100%); 34 | } 35 | } 36 | } 37 | 38 | @media screen and (min-width: 728px) { 39 | .users-list { 40 | padding: 40px 16px; 41 | } 42 | 43 | .post { 44 | width: calc((100% / 3) - 12px); 45 | } 46 | } 47 | 48 | .post__header, 49 | .post__header .user { 50 | align-items: center; 51 | display: flex; 52 | flex-direction: column; 53 | justify-content: center; 54 | padding: 0; 55 | width: 100%; 56 | } 57 | 58 | .post__header .user { 59 | padding: 15px; 60 | } 61 | 62 | .post__header .user__name { 63 | padding: 15px 0; 64 | font-size: 1.4rem; 65 | color: #858d93; 66 | transition: all ease-in-out .2s; 67 | } 68 | 69 | .user__thumb { 70 | background: linear-gradient(45deg, #08111e 0%, #08111e 100%); 71 | border: 2px solid transparent; 72 | height: 100px; 73 | margin: 0; 74 | transition: all ease-in-out .2s; 75 | width: 100px; 76 | } 77 | } 78 | 79 | @media screen and (min-width: 728px) { 80 | .users-list { 81 | padding: 16px 0; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/containers/UsersList/index.jsx: -------------------------------------------------------------------------------- 1 | import UsersList from './UsersList'; 2 | 3 | export default UsersList; 4 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import * as serviceWorker from './serviceWorker'; 5 | 6 | import App from './containers/App'; 7 | 8 | ReactDOM.render( 9 | , 10 | document.getElementById('root') 11 | ); 12 | 13 | // If you want your app to work offline and load faster, you can change 14 | // unregister() to register() below. Note this comes with some pitfalls. 15 | // Learn more about service workers: https://bit.ly/CRA-PWA 16 | serviceWorker.unregister(); 17 | -------------------------------------------------------------------------------- /src/modules/enpoints.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/src/modules/enpoints.js -------------------------------------------------------------------------------- /src/modules/formatter.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/src/modules/formatter.js -------------------------------------------------------------------------------- /src/modules/request.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenation-dev/react-instagram/2dc576d1dedf63cfd86c45ca5c2ce8371cdbb4d8/src/modules/request.js -------------------------------------------------------------------------------- /src/routes/FeedRoute/FeedRoute.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | import Stories from '../../containers/Stories'; 4 | import Loading from '../../components/Loading'; 5 | 6 | import Posts from '../../containers/Posts'; 7 | 8 | import './FeedRoute.scss'; 9 | 10 | const FeedRoute = () => { 11 | const [users, setUsers] = useState([]); 12 | const [posts, setPosts] = useState([]); 13 | const [stories, setStories] = useState([]); 14 | const [usersFetched, setUsersFetched] = useState(0); 15 | 16 | const getUserPostById = (postUserId) => users.find(user => postUserId === user.id); 17 | 18 | useEffect(() => { 19 | fetch('https://5e7d0266a917d70016684219.mockapi.io/api/v1/users') 20 | .then((res) => res.json()) 21 | .then(data => setUsers(data)); 22 | }, []); 23 | 24 | useEffect(() => { 25 | if (usersFetched === users.length) { 26 | return; 27 | } 28 | 29 | fetch(`https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/${users[usersFetched].id}/posts`) 30 | .then((res) => res.json()) 31 | .then(data => { 32 | setPosts([...posts, ...data]); 33 | setUsersFetched(usersFetched + 1); 34 | }); 35 | 36 | // eslint-disable-next-line react-hooks/exhaustive-deps 37 | }, [users, usersFetched]); 38 | 39 | useEffect(() => { 40 | fetch('https://5e7d0266a917d70016684219.mockapi.io/api/v1/stories') 41 | .then((res) => res.json()) 42 | .then(data => { 43 | setStories(data); 44 | }); 45 | }, [users]); 46 | 47 | return ( 48 |
49 | {(users.length > 0 && stories.length > 0) && ( 50 | 54 | )} 55 | 56 | {users.length !== usersFetched 57 | ? () 58 | : ( 59 | ) 63 | } 64 |
65 | ); 66 | }; 67 | 68 | export default FeedRoute; 69 | -------------------------------------------------------------------------------- /src/routes/FeedRoute/FeedRoute.scss: -------------------------------------------------------------------------------- 1 | .feed { 2 | display: flex; 3 | flex-direction: column; 4 | padding: 16px; 5 | position: relative; 6 | width: 100%; 7 | } 8 | 9 | @media screen and (min-width: 728px) { 10 | .feed { 11 | padding: 16px 0; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/routes/FeedRoute/index.jsx: -------------------------------------------------------------------------------- 1 | import FeedRoute from './FeedRoute'; 2 | 3 | export default FeedRoute; 4 | -------------------------------------------------------------------------------- /src/routes/NewUserRoute/NewUserRoute.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import UserForm from '../../containers/UserForm'; 4 | 5 | const NewUserRoute = () => ( 6 |
7 | 8 |
9 | ); 10 | 11 | export default NewUserRoute; 12 | -------------------------------------------------------------------------------- /src/routes/NewUserRoute/index.jsx: -------------------------------------------------------------------------------- 1 | import NewUserRoute from './NewUserRoute'; 2 | 3 | export default NewUserRoute; 4 | -------------------------------------------------------------------------------- /src/routes/ProfileRoute/ProfileRoute.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | import UserProfile from '../../containers/UserProfile'; 4 | import UserPosts from '../../containers/UserPosts'; 5 | 6 | import Loading from '../../components/Loading'; 7 | 8 | const ProfileRoute = () => { 9 | const [name, setName] = useState(''); 10 | const [id, setUserId] = useState(''); 11 | const [avatar, setAvatar] = useState(''); 12 | const [username, setUsername] = useState(''); 13 | const [email, setEmail] = useState(''); 14 | const [userPosts, setUserPosts] = useState([]); 15 | const [isLoading, setIsLoading] = useState(true); 16 | 17 | useEffect( 18 | () => { 19 | const { pathname } = window.location; 20 | const param = pathname.split("/")[2]; 21 | 22 | fetch(`https://5e7d0266a917d70016684219.mockapi.io/api/v1/users?search=${param}`) 23 | .then(response => response.json()) 24 | .then(profileData => { 25 | setAvatar(profileData[0].avatar); 26 | setEmail(profileData[0].email); 27 | setName(profileData[0].name); 28 | setUsername(profileData[0].username); 29 | setUserId(profileData[0].id); 30 | }); 31 | }, []); 32 | 33 | useEffect(() => { 34 | if (id) { 35 | fetch(`https://5e7d0266a917d70016684219.mockapi.io/api/v1/users/${id}/posts`) 36 | .then(response => response.json()) 37 | .then(posts => { 38 | setUserPosts(posts); 39 | setIsLoading(false) 40 | }); 41 | } 42 | }, [id]); 43 | 44 | return ( 45 |
46 | 52 | 53 | {isLoading 54 | ? () 55 | : 56 | } 57 |
58 | ); 59 | }; 60 | 61 | export default ProfileRoute; 62 | -------------------------------------------------------------------------------- /src/routes/ProfileRoute/index.jsx: -------------------------------------------------------------------------------- 1 | import ProfileRoute from './ProfileRoute'; 2 | 3 | export default ProfileRoute; 4 | -------------------------------------------------------------------------------- /src/routes/UsersRoute/UsersRoute.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | import UsersList from '../../containers/UsersList/UsersList'; 4 | 5 | const UsersRoute = () => { 6 | const [users, setUsers] = useState([]); 7 | 8 | useEffect(() => { 9 | fetch('https://5e7d0266a917d70016684219.mockapi.io/api/v1/users') 10 | .then((res) => res.json()) 11 | .then(data => setUsers(data)); 12 | }, []); 13 | 14 | return ( 15 |
16 | 17 |
18 | ); 19 | }; 20 | 21 | export default UsersRoute; 22 | -------------------------------------------------------------------------------- /src/routes/UsersRoute/index.jsx: -------------------------------------------------------------------------------- 1 | import UsersRoute from './UsersRoute'; 2 | 3 | export default UsersRoute; 4 | -------------------------------------------------------------------------------- /src/routes/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, Switch } from 'react-router-dom'; 3 | 4 | import FeedRoute from './FeedRoute'; 5 | import UsersRoute from './UsersRoute'; 6 | import ProfileRoute from './ProfileRoute'; 7 | import NewUserRoute from './NewUserRoute'; 8 | 9 | const Routes = () => ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | 29 | export default Routes; 30 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl, { 104 | headers: { 'Service-Worker': 'script' }, 105 | }) 106 | .then(response => { 107 | // Ensure service worker exists, and that we really are getting a JS file. 108 | const contentType = response.headers.get('content-type'); 109 | if ( 110 | response.status === 404 || 111 | (contentType != null && contentType.indexOf('javascript') === -1) 112 | ) { 113 | // No service worker found. Probably a different app. Reload the page. 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister().then(() => { 116 | window.location.reload(); 117 | }); 118 | }); 119 | } else { 120 | // Service worker found. Proceed as normal. 121 | registerValidSW(swUrl, config); 122 | } 123 | }) 124 | .catch(() => { 125 | console.log( 126 | 'No internet connection found. App is running in offline mode.' 127 | ); 128 | }); 129 | } 130 | 131 | export function unregister() { 132 | if ('serviceWorker' in navigator) { 133 | navigator.serviceWorker.ready 134 | .then(registration => { 135 | registration.unregister(); 136 | }) 137 | .catch(error => { 138 | console.error(error.message); 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | --------------------------------------------------------------------------------