├── .editorconfig ├── .eslintrc ├── .flowconfig ├── .gitignore ├── .lintstagedrc ├── .prettierrc ├── .travis.yml ├── README.md ├── flow-typed └── npm │ ├── @types │ └── jest_vx.x.x.js │ ├── babel-eslint_vx.x.x.js │ ├── camelcase-keys-deep_vx.x.x.js │ ├── eslint-cli_vx.x.x.js │ ├── eslint-config-react-app_vx.x.x.js │ ├── eslint-plugin-flowtype_vx.x.x.js │ ├── eslint_vx.x.x.js │ ├── file-loader_vx.x.x.js │ ├── flow-bin_v0.x.x.js │ ├── history_v4.x.x.js │ ├── husky_vx.x.x.js │ ├── jest-styled-components_vx.x.x.js │ ├── lint-staged_vx.x.x.js │ ├── lodash_v4.x.x.js │ ├── prettier_v1.x.x.js │ ├── react-event-listener_vx.x.x.js │ ├── react-helmet_v5.x.x.js │ ├── react-hot-loader_vx.x.x.js │ ├── react-onclickoutside_v6.x.x.js │ ├── react-redux_v5.x.x.js │ ├── react-responsive-masonry_vx.x.x.js │ ├── react-router-dom_v4.x.x.js │ ├── react-router-redux_vx.x.x.js │ ├── react-scripts_vx.x.x.js │ ├── react-test-renderer_v16.x.x.js │ ├── redux-devtools-extension_v2.x.x.js │ ├── redux-mock-store_v1.2.x.js │ ├── redux-saga_v0.16.x.js │ ├── redux_v3.x.x.js │ ├── styled-components_v3.x.x.js │ ├── url-search-params-polyfill_vx.x.x.js │ └── whatwg-fetch_vx.x.x.js ├── images ├── add-image-to-collections.png ├── create-new-collection.png ├── home-mobile.png ├── home.png └── search.png ├── jest.config.js ├── libdefs.js ├── package-lock.json ├── package.json ├── public ├── index.html ├── manifest.json └── statics │ ├── apple-touch-icon-114x114-precomposed.png │ ├── apple-touch-icon-120x120-precomposed.png │ ├── apple-touch-icon-144x144-precomposed.png │ ├── apple-touch-icon-152x152-precomposed.png │ ├── apple-touch-icon-60x60-precomposed.png │ ├── apple-touch-icon-72x72-precomposed.png │ ├── apple-touch-icon-76x76-precomposed.png │ ├── apple-touch-icon-precomposed.png │ ├── browserconfig.xml │ └── favicon.ico └── src ├── actions ├── app.js ├── app.test.js ├── collection.js ├── items.js ├── photo.js └── user.js ├── api ├── api-error.js ├── collection.js ├── photo.js ├── rest-helper.js └── user.js ├── components ├── Avatar │ └── index.js ├── Button │ └── index.js ├── Collection │ └── index.js ├── CollectionSView │ └── index.js ├── Collections │ └── index.js ├── CollectionsSView │ └── index.js ├── Dialog │ └── index.js ├── ExtLink │ └── index.js ├── Link │ └── index.js ├── NavOnAvatar │ └── index.js ├── Navigation │ └── index.js ├── Photo │ └── index.js ├── Photos │ └── index.js ├── Popover │ └── index.js ├── PrivateRoute │ └── index.js ├── Progress │ └── index.js ├── RTextInput │ └── index.js ├── SvgIcon │ └── index.js ├── SvgImage │ └── index.js ├── TextInput │ └── index.js ├── svg-icons │ ├── add.js │ ├── close.js │ ├── done.js │ ├── download.js │ ├── edit.js │ ├── like.js │ ├── lock.js │ └── remove.js └── svg-images │ └── camera.js ├── constants ├── action-types.js ├── api-error-codes.js └── service-info.js ├── containers ├── AddOrEditCollection │ └── index.js ├── AddOrEditCollectionDialog │ └── index.js ├── AddToCollection │ └── index.js ├── AddToCollectionDialog │ └── index.js ├── App │ └── index.js ├── Authorize │ └── index.js ├── Header │ └── index.js ├── Home │ └── index.js ├── LikedPhotos │ └── index.js ├── Login │ └── index.js ├── Logout │ └── index.js ├── MainApp │ └── index.js ├── NotFound │ └── index.js ├── PhotosByCollection │ └── index.js ├── Search │ └── index.js └── UserCollections │ └── index.js ├── index.js ├── reducers ├── app.js ├── app.test.js ├── index.js ├── items.js └── user.js ├── sagas ├── app.js ├── collection.js ├── index.js ├── photo.js ├── photo.test.js └── user.js ├── store.js ├── store.test.js ├── style ├── colors.js ├── global.js └── util.js └── types ├── action.js ├── data.js ├── index.js └── state.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | max_line_length = 80 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | max_line_length = 0 15 | trim_trailing_whitespace = false 16 | 17 | [COMMIT_EDITMSG] 18 | max_line_length = 0 19 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "react-app", 4 | "plugin:flowtype/recommended" 5 | ], 6 | "plugins": [ 7 | "flowtype" 8 | ] 9 | } -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/.* 3 | [include] 4 | 5 | [libs] 6 | ./libdefs.js 7 | 8 | [options] 9 | esproposal.class_static_fields=enable 10 | esproposal.class_instance_fields=enable 11 | 12 | [strict] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx,node,windows,intellij,visualstudiocode 3 | 4 | ### Intellij ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | # User-specific stuff: 9 | .idea/**/workspace.xml 10 | .idea/**/tasks.xml 11 | .idea/dictionaries 12 | 13 | # Sensitive or high-churn files: 14 | .idea/**/dataSources/ 15 | .idea/**/dataSources.ids 16 | .idea/**/dataSources.xml 17 | .idea/**/dataSources.local.xml 18 | .idea/**/sqlDataSources.xml 19 | .idea/**/dynamic.xml 20 | .idea/**/uiDesigner.xml 21 | 22 | # Gradle: 23 | .idea/**/gradle.xml 24 | .idea/**/libraries 25 | 26 | # CMake 27 | cmake-build-debug/ 28 | 29 | # Mongo Explorer plugin: 30 | .idea/**/mongoSettings.xml 31 | 32 | ## File-based project format: 33 | *.iws 34 | 35 | ## Plugin-specific files: 36 | 37 | # IntelliJ 38 | /out/ 39 | 40 | # mpeltonen/sbt-idea plugin 41 | .idea_modules/ 42 | 43 | # JIRA plugin 44 | atlassian-ide-plugin.xml 45 | 46 | # Cursive Clojure plugin 47 | .idea/replstate.xml 48 | 49 | # Crashlytics plugin (for Android Studio and IntelliJ) 50 | com_crashlytics_export_strings.xml 51 | crashlytics.properties 52 | crashlytics-build.properties 53 | fabric.properties 54 | 55 | ### Intellij Patch ### 56 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 57 | 58 | # *.iml 59 | # modules.xml 60 | # .idea/misc.xml 61 | # *.ipr 62 | 63 | # Sonarlint plugin 64 | .idea/sonarlint 65 | 66 | ### Node ### 67 | # Logs 68 | logs 69 | *.log 70 | npm-debug.log* 71 | yarn-debug.log* 72 | yarn-error.log* 73 | 74 | # Runtime data 75 | pids 76 | *.pid 77 | *.seed 78 | *.pid.lock 79 | 80 | # Directory for instrumented libs generated by jscoverage/JSCover 81 | lib-cov 82 | 83 | # Coverage directory used by tools like istanbul 84 | coverage 85 | 86 | # nyc test coverage 87 | .nyc_output 88 | 89 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 90 | .grunt 91 | 92 | # Bower dependency directory (https://bower.io/) 93 | bower_components 94 | 95 | # node-waf configuration 96 | .lock-wscript 97 | 98 | # Compiled binary addons (http://nodejs.org/api/addons.html) 99 | build/Release 100 | 101 | # Dependency directories 102 | node_modules/ 103 | jspm_packages/ 104 | 105 | # Typescript v1 declaration files 106 | typings/ 107 | 108 | # Optional npm cache directory 109 | .npm 110 | 111 | # Optional eslint cache 112 | .eslintcache 113 | 114 | # Optional REPL history 115 | .node_repl_history 116 | 117 | # Output of 'npm pack' 118 | *.tgz 119 | 120 | # Yarn Integrity file 121 | .yarn-integrity 122 | 123 | # dotenv environment variables file 124 | .env 125 | 126 | 127 | ### OSX ### 128 | *.DS_Store 129 | .AppleDouble 130 | .LSOverride 131 | 132 | # Icon must end with two \r 133 | Icon 134 | 135 | # Thumbnails 136 | ._* 137 | 138 | # Files that might appear in the root of a volume 139 | .DocumentRevisions-V100 140 | .fseventsd 141 | .Spotlight-V100 142 | .TemporaryItems 143 | .Trashes 144 | .VolumeIcon.icns 145 | .com.apple.timemachine.donotpresent 146 | 147 | # Directories potentially created on remote AFP share 148 | .AppleDB 149 | .AppleDesktop 150 | Network Trash Folder 151 | Temporary Items 152 | .apdisk 153 | 154 | ### VisualStudioCode ### 155 | .vscode/* 156 | !.vscode/settings.json 157 | !.vscode/tasks.json 158 | !.vscode/launch.json 159 | !.vscode/extensions.json 160 | 161 | ### Windows ### 162 | # Windows thumbnail cache files 163 | Thumbs.db 164 | ehthumbs.db 165 | ehthumbs_vista.db 166 | 167 | # Folder config file 168 | Desktop.ini 169 | 170 | # Recycle Bin used on file shares 171 | $RECYCLE.BIN/ 172 | 173 | # Windows Installer files 174 | *.cab 175 | *.msi 176 | *.msm 177 | *.msp 178 | 179 | # Windows shortcuts 180 | *.lnk 181 | 182 | # End of https://www.gitignore.io/api/osx,node,windows,intellij,visualstudiocode 183 | 184 | # since we put client information on environment variables file 185 | .env.development 186 | .env.production 187 | 188 | # production 189 | /build -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "src/**/*.js": [ 3 | "prettier --config ./.prettierrc --write", 4 | "eslint --fix", 5 | "git add" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": true, 3 | "jsxBracketSameLine": true, 4 | "printWidth": 80, 5 | "singleQuote": true, 6 | "trailingComma": "es5" 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | - "lts/*" 5 | cache: 6 | directories: 7 | - "node_modules" 8 | script: npm test -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/atahani/reactjs-unsplash.svg?branch=master)](https://travis-ci.org/atahani/reactjs-unsplash) 2 | 3 | # Unsplash Clone 4 | 5 | ## Introduction 6 | 7 | This project was developed to demonstrate how you can develop a React.js web application like [unsplash](https://unsplash.com/) from scratch. All of the components have been developed without any third parties library like [react-bootstrap](https://react-bootstrap.github.io/). If you understood Persian, you would be able to enroll this course in [Faranesh.com.](https://faranesh.com/web/16454-clone-unsplashcom-with-reactjs--redux) 8 | 9 | > This project has written only for educational purpose. So we don't want to abuse the name or idea of [unsplash.com](https://medium.com/unsplash/unsplash-api-guidelines-28e0216e6daa). 10 | 11 | ## How Does It Look Like? 12 | 13 | ![Home Page](images/home.png) 14 | ![Home Page in Mobile](images/home-mobile.png) 15 | ![Search Page](images/search.png) 16 | ![Create New Collection Dialog](images/create-new-collection.png) 17 | ![Add Image to Collections Dialog](images/add-image-to-collections.png) 18 | 19 | ## Some Libraries that have been used 20 | 21 | #### Flow 22 | 23 | Everybody nags about JavaScript is loosely typed language, and most of the bugs come from this feature. There are two ways to handle static type checking, [TypeScript](https://www.typescriptlang.org/), and [flow](http://flow.org/). I've used flow since I love ES6 syntax. 24 | 25 | #### Jest 26 | 27 | [Jest](https://facebook.github.io/jest/) is the best tool for testing React.js applications. It has some cool features like [Snapshot](https://facebook.github.io/jest/docs/en/snapshot-testing.html). 28 | Since this project was developed for educational, Only some tests have been written to demonstrate how tests should be by Jest library. 29 | 30 | #### styled-components 31 | 32 | There are many libraries to handle style in React. One of the best libraries is [styled-component](https://www.styled-components.com/) since you can manage style of components base on behaviors. 33 | 34 | #### Redux Persist 35 | 36 | [Redux](https://redux.js.org/) was used as store management Since we don't want to lose user informations such as user profile or access token. [Redux Persist](https://github.com/rt2zz/redux-persist) was used to store user state into local storage. The configuration is easier than you would think! 37 | 38 | ## Pre Requirements 39 | 40 | 1.create new application 41 | 42 | Since this project is based on [unspalsh API](https://unsplash.com/documentation) you need to have an application. Follow [this link](https://unsplash.com/oauth/applications/new) to create a new application. You need to set these permissions. 43 | 44 | ``` 45 | public 46 | read_photos 47 | write_likes 48 | read_collections 49 | write_collections 50 | ``` 51 | 52 | > NOTE: The `Redirect URI` is `http://localhost:3000/auth/callback` in development mode base on default settings. 53 | 54 | 2. create a file with `.env.development` OR `.env.production` name to set environment variables like `.env`. 55 | 56 | *For more information please see the [create-react-app documentation](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-custom-environment-variables).* 57 | 58 | ``` 59 | NODE_ENV=development 60 | 61 | REACT_APP_ROOT_URL=https://unsplash.com 62 | 63 | REACT_APP_API_ROOT=https://api.unsplash.com/ 64 | 65 | REACT_APP_CLIENT_ID=XXXXXXXXXXXXX 66 | 67 | REACT_APP_CLIENT_SECRET=XXXXXXXXXXXXX 68 | 69 | REACT_APP_OAUTH_PATH=https://unsplash.com/oauth/authorize?client_id=XXXXXXXXXXXXX&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth%2Fcallback&response_type=code&scope=public+read_photos+write_likes+read_collections+write_collections 70 | 71 | REACT_APP_REDIRECT_URI=http://localhost:3000/auth/callback 72 | ``` 73 | 74 | ## Project Commands 75 | 76 | #### Start App in Development Mode 77 | ``` 78 | npm run start 79 | ``` 80 | 81 | #### Test 82 | 83 | ``` 84 | npm run test 85 | ``` 86 | 87 | #### Test in Watch Mode 88 | 89 | ``` 90 | npm run test-w 91 | ``` 92 | 93 | #### Flow Checking 94 | 95 | ``` 96 | npm run flow 97 | ``` 98 | 99 | #### Linting 100 | 101 | ``` 102 | npm run lint 103 | ``` 104 | 105 | #### Build Project in Production Mode 106 | 107 | ``` 108 | npm run build 109 | ``` 110 | 111 | #### Build and Run Server 112 | 113 | ``` 114 | npm run build-and-run-server 115 | ``` 116 | 117 | #### Run Server Only 118 | 119 | ``` 120 | npm run run-server 121 | ``` -------------------------------------------------------------------------------- /flow-typed/npm/@types/jest_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 7e545291eca1b32ed268936b11e159b8 2 | // flow-typed version: <>/@types/jest_v^22.0.1/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * '@types/jest' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module '@types/jest' { 17 | declare module.exports: any; 18 | } 19 | -------------------------------------------------------------------------------- /flow-typed/npm/babel-eslint_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 61231b0c1466c88334b826bc74f91add 2 | // flow-typed version: <>/babel-eslint_v^10.0.0/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'babel-eslint' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'babel-eslint' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'babel-eslint/lib/analyze-scope' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'babel-eslint/lib/babylon-to-espree/attachComments' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'babel-eslint/lib/babylon-to-espree/convertComments' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'babel-eslint/lib/babylon-to-espree/convertTemplateType' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'babel-eslint/lib/babylon-to-espree/index' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'babel-eslint/lib/babylon-to-espree/toAST' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'babel-eslint/lib/babylon-to-espree/toToken' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'babel-eslint/lib/babylon-to-espree/toTokens' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'babel-eslint/lib/index' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'babel-eslint/lib/parse-with-scope' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'babel-eslint/lib/parse' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'babel-eslint/lib/visitor-keys' { 70 | declare module.exports: any; 71 | } 72 | 73 | // Filename aliases 74 | declare module 'babel-eslint/lib/analyze-scope.js' { 75 | declare module.exports: $Exports<'babel-eslint/lib/analyze-scope'>; 76 | } 77 | declare module 'babel-eslint/lib/babylon-to-espree/attachComments.js' { 78 | declare module.exports: $Exports<'babel-eslint/lib/babylon-to-espree/attachComments'>; 79 | } 80 | declare module 'babel-eslint/lib/babylon-to-espree/convertComments.js' { 81 | declare module.exports: $Exports<'babel-eslint/lib/babylon-to-espree/convertComments'>; 82 | } 83 | declare module 'babel-eslint/lib/babylon-to-espree/convertTemplateType.js' { 84 | declare module.exports: $Exports<'babel-eslint/lib/babylon-to-espree/convertTemplateType'>; 85 | } 86 | declare module 'babel-eslint/lib/babylon-to-espree/index.js' { 87 | declare module.exports: $Exports<'babel-eslint/lib/babylon-to-espree/index'>; 88 | } 89 | declare module 'babel-eslint/lib/babylon-to-espree/toAST.js' { 90 | declare module.exports: $Exports<'babel-eslint/lib/babylon-to-espree/toAST'>; 91 | } 92 | declare module 'babel-eslint/lib/babylon-to-espree/toToken.js' { 93 | declare module.exports: $Exports<'babel-eslint/lib/babylon-to-espree/toToken'>; 94 | } 95 | declare module 'babel-eslint/lib/babylon-to-espree/toTokens.js' { 96 | declare module.exports: $Exports<'babel-eslint/lib/babylon-to-espree/toTokens'>; 97 | } 98 | declare module 'babel-eslint/lib/index.js' { 99 | declare module.exports: $Exports<'babel-eslint/lib/index'>; 100 | } 101 | declare module 'babel-eslint/lib/parse-with-scope.js' { 102 | declare module.exports: $Exports<'babel-eslint/lib/parse-with-scope'>; 103 | } 104 | declare module 'babel-eslint/lib/parse.js' { 105 | declare module.exports: $Exports<'babel-eslint/lib/parse'>; 106 | } 107 | declare module 'babel-eslint/lib/visitor-keys.js' { 108 | declare module.exports: $Exports<'babel-eslint/lib/visitor-keys'>; 109 | } 110 | -------------------------------------------------------------------------------- /flow-typed/npm/camelcase-keys-deep_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: a6e0eab497fc5c9e6e830ebc54b8f6fb 2 | // flow-typed version: <>/camelcase-keys-deep_v^0.1.0/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'camelcase-keys-deep' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'camelcase-keys-deep' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | 26 | 27 | // Filename aliases 28 | declare module 'camelcase-keys-deep/index' { 29 | declare module.exports: $Exports<'camelcase-keys-deep'>; 30 | } 31 | declare module 'camelcase-keys-deep/index.js' { 32 | declare module.exports: $Exports<'camelcase-keys-deep'>; 33 | } 34 | -------------------------------------------------------------------------------- /flow-typed/npm/eslint-cli_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: c2f2bd0b0b05be503a52e381cc980a46 2 | // flow-typed version: <>/eslint-cli_v^1.1.1/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'eslint-cli' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'eslint-cli' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'eslint-cli/bin/eslint' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'eslint-cli/lib/get-bin-eslint-js' { 30 | declare module.exports: any; 31 | } 32 | 33 | // Filename aliases 34 | declare module 'eslint-cli/bin/eslint.js' { 35 | declare module.exports: $Exports<'eslint-cli/bin/eslint'>; 36 | } 37 | declare module 'eslint-cli/lib/get-bin-eslint-js.js' { 38 | declare module.exports: $Exports<'eslint-cli/lib/get-bin-eslint-js'>; 39 | } 40 | -------------------------------------------------------------------------------- /flow-typed/npm/eslint-config-react-app_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 3ab5504df971a770e8c98e2533f0bbbd 2 | // flow-typed version: <>/eslint-config-react-app_v^2.1.0/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'eslint-config-react-app' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'eslint-config-react-app' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | 26 | 27 | // Filename aliases 28 | declare module 'eslint-config-react-app/index' { 29 | declare module.exports: $Exports<'eslint-config-react-app'>; 30 | } 31 | declare module 'eslint-config-react-app/index.js' { 32 | declare module.exports: $Exports<'eslint-config-react-app'>; 33 | } 34 | -------------------------------------------------------------------------------- /flow-typed/npm/file-loader_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 740bd9e0ab9450f4edb4f7ba932e41fa 2 | // flow-typed version: <>/file-loader_v^1.1.6/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'file-loader' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'file-loader' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'file-loader/dist/cjs' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'file-loader/dist/index' { 30 | declare module.exports: any; 31 | } 32 | 33 | // Filename aliases 34 | declare module 'file-loader/dist/cjs.js' { 35 | declare module.exports: $Exports<'file-loader/dist/cjs'>; 36 | } 37 | declare module 'file-loader/dist/index.js' { 38 | declare module.exports: $Exports<'file-loader/dist/index'>; 39 | } 40 | -------------------------------------------------------------------------------- /flow-typed/npm/flow-bin_v0.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 6a5610678d4b01e13bbfbbc62bdaf583 2 | // flow-typed version: 3817bc6980/flow-bin_v0.x.x/flow_>=v0.25.x 3 | 4 | declare module "flow-bin" { 5 | declare module.exports: string; 6 | } 7 | -------------------------------------------------------------------------------- /flow-typed/npm/history_v4.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 7d9c626f1773c253c237fa37953ae00d 2 | // flow-typed version: 9cb16b6503/history_v4.x.x/flow_>=v0.25.x 3 | 4 | declare module "history/createBrowserHistory" { 5 | declare function Unblock(): void; 6 | 7 | declare export type Action = "PUSH" | "REPLACE" | "POP"; 8 | 9 | declare export type BrowserLocation = { 10 | pathname: string, 11 | search: string, 12 | hash: string, 13 | // Browser and Memory specific 14 | state: {}, 15 | key: string, 16 | }; 17 | 18 | declare interface IBrowserHistory { 19 | length: number, 20 | location: BrowserLocation, 21 | action: Action, 22 | push(path: string, state?: {}): void, 23 | push(location: $Shape): void, 24 | replace(path: string, state?: {}): void, 25 | replace(location: $Shape): void, 26 | go(n: number): void, 27 | goBack(): void, 28 | goForward(): void, 29 | listen: Function, 30 | block(message: string): Unblock, 31 | block((location: BrowserLocation, action: Action) => string): Unblock, 32 | } 33 | 34 | declare export type BrowserHistory = IBrowserHistory; 35 | 36 | declare type HistoryOpts = { 37 | basename?: string, 38 | forceRefresh?: boolean, 39 | getUserConfirmation?: ( 40 | message: string, 41 | callback: (willContinue: boolean) => void, 42 | ) => void, 43 | }; 44 | 45 | declare export default (opts?: HistoryOpts) => BrowserHistory; 46 | } 47 | 48 | declare module "history/createMemoryHistory" { 49 | declare function Unblock(): void; 50 | 51 | declare export type Action = "PUSH" | "REPLACE" | "POP"; 52 | 53 | declare export type MemoryLocation = { 54 | pathname: string, 55 | search: string, 56 | hash: string, 57 | // Browser and Memory specific 58 | state: {}, 59 | key: string, 60 | }; 61 | 62 | declare interface IMemoryHistory { 63 | length: number, 64 | location: MemoryLocation, 65 | action: Action, 66 | index: number, 67 | entries: Array, 68 | push(path: string, state?: {}): void, 69 | push(location: $Shape): void, 70 | replace(path: string, state?: {}): void, 71 | replace(location: $Shape): void, 72 | go(n: number): void, 73 | goBack(): void, 74 | goForward(): void, 75 | // Memory only 76 | canGo(n: number): boolean, 77 | listen: Function, 78 | block(message: string): Unblock, 79 | block((location: MemoryLocation, action: Action) => string): Unblock, 80 | } 81 | 82 | declare export type MemoryHistory = IMemoryHistory; 83 | 84 | declare type HistoryOpts = { 85 | initialEntries?: Array, 86 | initialIndex?: number, 87 | keyLength?: number, 88 | getUserConfirmation?: ( 89 | message: string, 90 | callback: (willContinue: boolean) => void, 91 | ) => void, 92 | }; 93 | 94 | declare export default (opts?: HistoryOpts) => MemoryHistory; 95 | } 96 | 97 | declare module "history/createHashHistory" { 98 | declare function Unblock(): void; 99 | 100 | declare export type Action = "PUSH" | "REPLACE" | "POP"; 101 | 102 | declare export type HashLocation = { 103 | pathname: string, 104 | search: string, 105 | hash: string, 106 | }; 107 | 108 | declare interface IHashHistory { 109 | length: number, 110 | location: HashLocation, 111 | action: Action, 112 | push(path: string, state?: {}): void, 113 | push(location: $Shape): void, 114 | replace(path: string, state?: {}): void, 115 | replace(location: $Shape): void, 116 | go(n: number): void, 117 | goBack(): void, 118 | goForward(): void, 119 | listen: Function, 120 | block(message: string): Unblock, 121 | block((location: HashLocation, action: Action) => string): Unblock, 122 | push(path: string): void, 123 | } 124 | 125 | declare export type HashHistory = IHashHistory; 126 | 127 | declare type HistoryOpts = { 128 | basename?: string, 129 | hashType: "slash" | "noslash" | "hashbang", 130 | getUserConfirmation?: ( 131 | message: string, 132 | callback: (willContinue: boolean) => void, 133 | ) => void, 134 | }; 135 | 136 | declare export default (opts?: HistoryOpts) => HashHistory; 137 | } 138 | -------------------------------------------------------------------------------- /flow-typed/npm/husky_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: ceafdba6372cd7becddaf4615095dab1 2 | // flow-typed version: <>/husky_v^1.0.0/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'husky' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'husky' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'husky/husky' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'husky/lib/getConf' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'husky/lib/installer/bin' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'husky/lib/installer/getScript' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'husky/lib/installer/index' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'husky/lib/installer/is' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'husky/lib/installer/resolveGitDir' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'husky/lib/runner/bin' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'husky/lib/runner/index' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'husky/lib/upgrader/bin' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'husky/lib/upgrader/index' { 66 | declare module.exports: any; 67 | } 68 | 69 | // Filename aliases 70 | declare module 'husky/husky.js' { 71 | declare module.exports: $Exports<'husky/husky'>; 72 | } 73 | declare module 'husky/lib/getConf.js' { 74 | declare module.exports: $Exports<'husky/lib/getConf'>; 75 | } 76 | declare module 'husky/lib/installer/bin.js' { 77 | declare module.exports: $Exports<'husky/lib/installer/bin'>; 78 | } 79 | declare module 'husky/lib/installer/getScript.js' { 80 | declare module.exports: $Exports<'husky/lib/installer/getScript'>; 81 | } 82 | declare module 'husky/lib/installer/index.js' { 83 | declare module.exports: $Exports<'husky/lib/installer/index'>; 84 | } 85 | declare module 'husky/lib/installer/is.js' { 86 | declare module.exports: $Exports<'husky/lib/installer/is'>; 87 | } 88 | declare module 'husky/lib/installer/resolveGitDir.js' { 89 | declare module.exports: $Exports<'husky/lib/installer/resolveGitDir'>; 90 | } 91 | declare module 'husky/lib/runner/bin.js' { 92 | declare module.exports: $Exports<'husky/lib/runner/bin'>; 93 | } 94 | declare module 'husky/lib/runner/index.js' { 95 | declare module.exports: $Exports<'husky/lib/runner/index'>; 96 | } 97 | declare module 'husky/lib/upgrader/bin.js' { 98 | declare module.exports: $Exports<'husky/lib/upgrader/bin'>; 99 | } 100 | declare module 'husky/lib/upgrader/index.js' { 101 | declare module.exports: $Exports<'husky/lib/upgrader/index'>; 102 | } 103 | -------------------------------------------------------------------------------- /flow-typed/npm/jest-styled-components_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: c991e193c5c2af958301e9fe099b02c6 2 | // flow-typed version: <>/jest-styled-components_v^6.2.1/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'jest-styled-components' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'jest-styled-components' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'jest-styled-components/native/index' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'jest-styled-components/serializer/index' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'jest-styled-components/src/index' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'jest-styled-components/src/native/toHaveStyleRule' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'jest-styled-components/src/styleSheetSerializer' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'jest-styled-components/src/toHaveStyleRule' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'jest-styled-components/src/utils' { 50 | declare module.exports: any; 51 | } 52 | 53 | // Filename aliases 54 | declare module 'jest-styled-components/native/index.js' { 55 | declare module.exports: $Exports<'jest-styled-components/native/index'>; 56 | } 57 | declare module 'jest-styled-components/serializer/index.js' { 58 | declare module.exports: $Exports<'jest-styled-components/serializer/index'>; 59 | } 60 | declare module 'jest-styled-components/src/index.js' { 61 | declare module.exports: $Exports<'jest-styled-components/src/index'>; 62 | } 63 | declare module 'jest-styled-components/src/native/toHaveStyleRule.js' { 64 | declare module.exports: $Exports<'jest-styled-components/src/native/toHaveStyleRule'>; 65 | } 66 | declare module 'jest-styled-components/src/styleSheetSerializer.js' { 67 | declare module.exports: $Exports<'jest-styled-components/src/styleSheetSerializer'>; 68 | } 69 | declare module 'jest-styled-components/src/toHaveStyleRule.js' { 70 | declare module.exports: $Exports<'jest-styled-components/src/toHaveStyleRule'>; 71 | } 72 | declare module 'jest-styled-components/src/utils.js' { 73 | declare module.exports: $Exports<'jest-styled-components/src/utils'>; 74 | } 75 | -------------------------------------------------------------------------------- /flow-typed/npm/lint-staged_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 8f4a5c86d1a23c2a8b1f9956f9ae2618 2 | // flow-typed version: <>/lint-staged_v^7.3.0/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'lint-staged' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'lint-staged' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'lint-staged/src/calcChunkSize' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'lint-staged/src/checkPkgScripts' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'lint-staged/src/findBin' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'lint-staged/src/generateTasks' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'lint-staged/src/getConfig' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'lint-staged/src/index' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'lint-staged/src/makeCmdTasks' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'lint-staged/src/printErrors' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'lint-staged/src/resolveGitDir' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'lint-staged/src/resolveTaskFn' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'lint-staged/src/runAll' { 66 | declare module.exports: any; 67 | } 68 | 69 | // Filename aliases 70 | declare module 'lint-staged/index' { 71 | declare module.exports: $Exports<'lint-staged'>; 72 | } 73 | declare module 'lint-staged/index.js' { 74 | declare module.exports: $Exports<'lint-staged'>; 75 | } 76 | declare module 'lint-staged/src/calcChunkSize.js' { 77 | declare module.exports: $Exports<'lint-staged/src/calcChunkSize'>; 78 | } 79 | declare module 'lint-staged/src/checkPkgScripts.js' { 80 | declare module.exports: $Exports<'lint-staged/src/checkPkgScripts'>; 81 | } 82 | declare module 'lint-staged/src/findBin.js' { 83 | declare module.exports: $Exports<'lint-staged/src/findBin'>; 84 | } 85 | declare module 'lint-staged/src/generateTasks.js' { 86 | declare module.exports: $Exports<'lint-staged/src/generateTasks'>; 87 | } 88 | declare module 'lint-staged/src/getConfig.js' { 89 | declare module.exports: $Exports<'lint-staged/src/getConfig'>; 90 | } 91 | declare module 'lint-staged/src/index.js' { 92 | declare module.exports: $Exports<'lint-staged/src/index'>; 93 | } 94 | declare module 'lint-staged/src/makeCmdTasks.js' { 95 | declare module.exports: $Exports<'lint-staged/src/makeCmdTasks'>; 96 | } 97 | declare module 'lint-staged/src/printErrors.js' { 98 | declare module.exports: $Exports<'lint-staged/src/printErrors'>; 99 | } 100 | declare module 'lint-staged/src/resolveGitDir.js' { 101 | declare module.exports: $Exports<'lint-staged/src/resolveGitDir'>; 102 | } 103 | declare module 'lint-staged/src/resolveTaskFn.js' { 104 | declare module.exports: $Exports<'lint-staged/src/resolveTaskFn'>; 105 | } 106 | declare module 'lint-staged/src/runAll.js' { 107 | declare module.exports: $Exports<'lint-staged/src/runAll'>; 108 | } 109 | -------------------------------------------------------------------------------- /flow-typed/npm/prettier_v1.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 4eed8da2dc730dc33e7710b465eaa44b 2 | // flow-typed version: cc7a557b34/prettier_v1.x.x/flow_>=v0.56.x 3 | 4 | declare module "prettier" { 5 | declare type AST = Object; 6 | declare type Doc = Object; 7 | declare type FastPath = Object; 8 | 9 | declare type PrettierParserName = 10 | | "babylon" 11 | | "flow" 12 | | "typescript" 13 | | "postcss" 14 | | "css" 15 | | "less" 16 | | "scss" 17 | | "json" 18 | | "graphql" 19 | | "markdown" 20 | | "vue"; 21 | 22 | declare type PrettierParser = { 23 | [name: PrettierParserName]: (text: string, options?: Object) => AST 24 | }; 25 | 26 | declare type CustomParser = ( 27 | text: string, 28 | parsers: PrettierParser, 29 | options: Options 30 | ) => AST; 31 | 32 | declare type Options = {| 33 | printWidth?: number, 34 | tabWidth?: number, 35 | useTabs?: boolean, 36 | semi?: boolean, 37 | singleQuote?: boolean, 38 | trailingComma?: "none" | "es5" | "all", 39 | bracketSpacing?: boolean, 40 | jsxBracketSameLine?: boolean, 41 | arrowParens?: "avoid" | "always", 42 | rangeStart?: number, 43 | rangeEnd?: number, 44 | parser?: PrettierParserName | CustomParser, 45 | filepath?: string, 46 | requirePragma?: boolean, 47 | insertPragma?: boolean, 48 | proseWrap?: "always" | "never" | "preserve", 49 | plugins?: Array 50 | |}; 51 | 52 | declare type Plugin = { 53 | languages: SupportLanguage, 54 | parsers: { [parserName: string]: Parser }, 55 | printers: { [astFormat: string]: Printer } 56 | }; 57 | 58 | declare type Parser = { 59 | parse: ( 60 | text: string, 61 | parsers: { [parserName: string]: Parser }, 62 | options: Object 63 | ) => AST, 64 | astFormat: string 65 | }; 66 | 67 | declare type Printer = { 68 | print: ( 69 | path: FastPath, 70 | options: Object, 71 | print: (path: FastPath) => Doc 72 | ) => Doc, 73 | embed: ( 74 | path: FastPath, 75 | print: (path: FastPath) => Doc, 76 | textToDoc: (text: string, options: Object) => Doc, 77 | options: Object 78 | ) => ?Doc 79 | }; 80 | 81 | declare type CursorOptions = {| 82 | cursorOffset: number, 83 | printWidth?: $PropertyType, 84 | tabWidth?: $PropertyType, 85 | useTabs?: $PropertyType, 86 | semi?: $PropertyType, 87 | singleQuote?: $PropertyType, 88 | trailingComma?: $PropertyType, 89 | bracketSpacing?: $PropertyType, 90 | jsxBracketSameLine?: $PropertyType, 91 | arrowParens?: $PropertyType, 92 | parser?: $PropertyType, 93 | filepath?: $PropertyType, 94 | requirePragma?: $PropertyType, 95 | insertPragma?: $PropertyType, 96 | proseWrap?: $PropertyType, 97 | plugins?: $PropertyType 98 | |}; 99 | 100 | declare type CursorResult = {| 101 | formatted: string, 102 | cursorOffset: number 103 | |}; 104 | 105 | declare type ResolveConfigOptions = {| 106 | useCache?: boolean, 107 | config?: string, 108 | editorconfig?: boolean 109 | |}; 110 | 111 | declare type SupportLanguage = { 112 | name: string, 113 | since: string, 114 | parsers: Array, 115 | group?: string, 116 | tmScope: string, 117 | aceMode: string, 118 | codemirrorMode: string, 119 | codemirrorMimeType: string, 120 | aliases?: Array, 121 | extensions: Array, 122 | filenames?: Array, 123 | linguistLanguageId: number, 124 | vscodeLanguageIds: Array 125 | }; 126 | 127 | declare type SupportOption = {| 128 | since: string, 129 | type: "int" | "boolean" | "choice" | "path", 130 | deprecated?: string, 131 | redirect?: SupportOptionRedirect, 132 | description: string, 133 | oppositeDescription?: string, 134 | default: SupportOptionValue, 135 | range?: SupportOptionRange, 136 | choices?: SupportOptionChoice 137 | |}; 138 | 139 | declare type SupportOptionRedirect = {| 140 | options: string, 141 | value: SupportOptionValue 142 | |}; 143 | 144 | declare type SupportOptionRange = {| 145 | start: number, 146 | end: number, 147 | step: number 148 | |}; 149 | 150 | declare type SupportOptionChoice = {| 151 | value: boolean | string, 152 | description?: string, 153 | since?: string, 154 | deprecated?: string, 155 | redirect?: SupportOptionValue 156 | |}; 157 | 158 | declare type SupportOptionValue = number | boolean | string; 159 | 160 | declare type SupportInfo = {| 161 | languages: Array, 162 | options: Array 163 | |}; 164 | 165 | declare type Prettier = {| 166 | format: (source: string, options?: Options) => string, 167 | check: (source: string, options?: Options) => boolean, 168 | formatWithCursor: (source: string, options: CursorOptions) => CursorResult, 169 | resolveConfig: { 170 | (filePath: string, options?: ResolveConfigOptions): Promise, 171 | sync(filePath: string, options?: ResolveConfigOptions): Promise 172 | }, 173 | clearConfigCache: () => void, 174 | getSupportInfo: (version?: string) => SupportInfo 175 | |}; 176 | 177 | declare export default Prettier; 178 | } 179 | -------------------------------------------------------------------------------- /flow-typed/npm/react-event-listener_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 18c75421142cfabb9441d107328550f0 2 | // flow-typed version: <>/react-event-listener_v^0.5.3/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'react-event-listener' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'react-event-listener' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'react-event-listener/dist/react-event-listener.cjs' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'react-event-listener/dist/react-event-listener.umd' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'react-event-listener/src/index' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'react-event-listener/src/supports' { 38 | declare module.exports: any; 39 | } 40 | 41 | // Filename aliases 42 | declare module 'react-event-listener/dist/react-event-listener.cjs.js' { 43 | declare module.exports: $Exports<'react-event-listener/dist/react-event-listener.cjs'>; 44 | } 45 | declare module 'react-event-listener/dist/react-event-listener.umd.js' { 46 | declare module.exports: $Exports<'react-event-listener/dist/react-event-listener.umd'>; 47 | } 48 | declare module 'react-event-listener/src/index.js' { 49 | declare module.exports: $Exports<'react-event-listener/src/index'>; 50 | } 51 | declare module 'react-event-listener/src/supports.js' { 52 | declare module.exports: $Exports<'react-event-listener/src/supports'>; 53 | } 54 | -------------------------------------------------------------------------------- /flow-typed/npm/react-helmet_v5.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: afa3502910d5b2aef93707cc683f52b8 2 | // flow-typed version: 492c298a82/react-helmet_v5.x.x/flow_>=v0.53.x 3 | 4 | declare module 'react-helmet' { 5 | declare type Props = { 6 | base?: Object, 7 | bodyAttributes?: Object, 8 | children?: React$Node, 9 | defaultTitle?: string, 10 | defer?: boolean, 11 | encodeSpecialCharacters?: boolean, 12 | htmlAttributes?: Object, 13 | link?: Array, 14 | meta?: Array, 15 | noscript?: Array, 16 | onChangeClientState?: ( 17 | newState?: Object, 18 | addedTags?: Object, 19 | removeTags?: Object 20 | ) => any, 21 | script?: Array, 22 | style?: Array, 23 | title?: string, 24 | titleAttributes?: Object, 25 | titleTemplate?: string, 26 | } 27 | 28 | declare interface TagMethods { 29 | toString(): string; 30 | toComponent(): [React$Element<*>] | React$Element<*> | Array; 31 | } 32 | 33 | declare interface AttributeTagMethods { 34 | toString(): string; 35 | toComponent(): {[string]: *}; 36 | } 37 | 38 | declare interface StateOnServer { 39 | base: TagMethods; 40 | bodyAttributes: AttributeTagMethods, 41 | htmlAttributes: AttributeTagMethods; 42 | link: TagMethods; 43 | meta: TagMethods; 44 | noscript: TagMethods; 45 | script: TagMethods; 46 | style: TagMethods; 47 | title: TagMethods; 48 | } 49 | 50 | declare class Helmet extends React$Component { 51 | static rewind(): StateOnServer; 52 | static renderStatic(): StateOnServer; 53 | static canUseDom(canUseDOM: boolean): void; 54 | } 55 | 56 | declare export default typeof Helmet 57 | declare export var Helmet: typeof Helmet 58 | } 59 | 60 | -------------------------------------------------------------------------------- /flow-typed/npm/react-onclickoutside_v6.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 6eb42473f8963358a108bc3234413fa1 2 | // flow-typed version: 83f69ebb56/react-onclickoutside_v6.x.x/flow_>=v0.54.1 3 | 4 | declare module 'react-onclickoutside' { 5 | declare export type OnClickOutsideProps = { 6 | eventTypes?: Array, 7 | outsideClickIgnoreClass?: string, 8 | preventDefault?: boolean, 9 | stopPropagation?: boolean 10 | }; 11 | 12 | declare export var IGNORE_CLASS_NAME: string; 13 | 14 | declare export default ( 15 | BaseComponent: Class>, 16 | config?: { excludeScrollbar?: boolean } 17 | ) => React$ComponentType

; 21 | } 22 | -------------------------------------------------------------------------------- /flow-typed/npm/react-responsive-masonry_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: fba9140fac9f0b5129ffda701d737e41 2 | // flow-typed version: <>/react-responsive-masonry_v^2.0.0/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'react-responsive-masonry' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'react-responsive-masonry' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'react-responsive-masonry/es/index' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'react-responsive-masonry/es/Masonry/index' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'react-responsive-masonry/es/ResponsiveMasonry/index' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'react-responsive-masonry/lib/index' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'react-responsive-masonry/lib/Masonry/index' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'react-responsive-masonry/lib/ResponsiveMasonry/index' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'react-responsive-masonry/umd/react-responsive-masonry' { 50 | declare module.exports: any; 51 | } 52 | 53 | // Filename aliases 54 | declare module 'react-responsive-masonry/es/index.js' { 55 | declare module.exports: $Exports<'react-responsive-masonry/es/index'>; 56 | } 57 | declare module 'react-responsive-masonry/es/Masonry/index.js' { 58 | declare module.exports: $Exports<'react-responsive-masonry/es/Masonry/index'>; 59 | } 60 | declare module 'react-responsive-masonry/es/ResponsiveMasonry/index.js' { 61 | declare module.exports: $Exports<'react-responsive-masonry/es/ResponsiveMasonry/index'>; 62 | } 63 | declare module 'react-responsive-masonry/lib/index.js' { 64 | declare module.exports: $Exports<'react-responsive-masonry/lib/index'>; 65 | } 66 | declare module 'react-responsive-masonry/lib/Masonry/index.js' { 67 | declare module.exports: $Exports<'react-responsive-masonry/lib/Masonry/index'>; 68 | } 69 | declare module 'react-responsive-masonry/lib/ResponsiveMasonry/index.js' { 70 | declare module.exports: $Exports<'react-responsive-masonry/lib/ResponsiveMasonry/index'>; 71 | } 72 | declare module 'react-responsive-masonry/umd/react-responsive-masonry.js' { 73 | declare module.exports: $Exports<'react-responsive-masonry/umd/react-responsive-masonry'>; 74 | } 75 | -------------------------------------------------------------------------------- /flow-typed/npm/react-router-dom_v4.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 53be1849af6037db65e90a7abc558afe 2 | // flow-typed version: f4e99ca1ed/react-router-dom_v4.x.x/flow_>=v0.63.x 3 | 4 | declare module "react-router-dom" { 5 | import type { ComponentType, ElementConfig, Node, Component } from 'react'; 6 | 7 | declare export var BrowserRouter: Class> 14 | 15 | declare export var HashRouter: Class> 21 | 22 | declare export var Link: Class> 28 | 29 | declare export var NavLink: Class boolean, 36 | children?: Node, 37 | exact?: boolean, 38 | strict?: boolean 39 | }>> 40 | 41 | // NOTE: Below are duplicated from react-router. If updating these, please 42 | // update the react-router and react-router-native types as well. 43 | declare export type Location = { 44 | pathname: string, 45 | search: string, 46 | hash: string, 47 | state?: any, 48 | key?: string 49 | }; 50 | 51 | declare export type LocationShape = { 52 | pathname?: string, 53 | search?: string, 54 | hash?: string, 55 | state?: any 56 | }; 57 | 58 | declare export type HistoryAction = "PUSH" | "REPLACE" | "POP"; 59 | 60 | declare export type RouterHistory = { 61 | length: number, 62 | location: Location, 63 | action: HistoryAction, 64 | listen( 65 | callback: (location: Location, action: HistoryAction) => void 66 | ): () => void, 67 | push(path: string | LocationShape, state?: any): void, 68 | replace(path: string | LocationShape, state?: any): void, 69 | go(n: number): void, 70 | goBack(): void, 71 | goForward(): void, 72 | canGo?: (n: number) => boolean, 73 | block( 74 | callback: (location: Location, action: HistoryAction) => boolean 75 | ): void, 76 | // createMemoryHistory 77 | index?: number, 78 | entries?: Array 79 | }; 80 | 81 | declare export type Match = { 82 | params: { [key: string]: ?string }, 83 | isExact: boolean, 84 | path: string, 85 | url: string 86 | }; 87 | 88 | declare export type ContextRouter = {| 89 | history: RouterHistory, 90 | location: Location, 91 | match: Match, 92 | staticContext?: StaticRouterContext 93 | |}; 94 | 95 | declare type ContextRouterVoid = { 96 | history: RouterHistory | void, 97 | location: Location | void, 98 | match: Match | void, 99 | staticContext?: StaticRouterContext | void 100 | }; 101 | 102 | declare export type GetUserConfirmation = ( 103 | message: string, 104 | callback: (confirmed: boolean) => void 105 | ) => void; 106 | 107 | declare export type StaticRouterContext = { 108 | url?: string 109 | }; 110 | 111 | declare export var StaticRouter: Class> 117 | 118 | declare export var MemoryRouter: Class, 120 | initialIndex?: number, 121 | getUserConfirmation?: GetUserConfirmation, 122 | keyLength?: number, 123 | children?: Node 124 | |}>> 125 | 126 | declare export var Router: Class> 130 | 131 | declare export var Prompt: Class string | boolean), 133 | when?: boolean 134 | |}>> 135 | 136 | declare export var Redirect: Class> 143 | 144 | declare export var Route: Class, 146 | render?: (router: ContextRouter) => Node, 147 | children?: ComponentType | Node, 148 | path?: string, 149 | exact?: boolean, 150 | strict?: boolean, 151 | location?: LocationShape, 152 | sensitive?: boolean 153 | |}>> 154 | 155 | declare export var Switch: Class> 159 | 160 | declare export function withRouter>( 161 | Component: WrappedComponent 162 | ): ComponentType< 163 | $Diff>, ContextRouterVoid> 164 | >; 165 | 166 | declare type MatchPathOptions = { 167 | path?: string, 168 | exact?: boolean, 169 | sensitive?: boolean, 170 | strict?: boolean 171 | }; 172 | 173 | declare export function matchPath( 174 | pathname: string, 175 | options?: MatchPathOptions | string, 176 | parent?: Match 177 | ): null | Match; 178 | 179 | declare export function generatePath(pattern?: string, params?: Object): string; 180 | } 181 | -------------------------------------------------------------------------------- /flow-typed/npm/react-router-redux_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: d2b057336c0342232ab7e0b7b808cedf 2 | // flow-typed version: <>/react-router-redux_vnext/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'react-router-redux' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'react-router-redux' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'react-router-redux/actions' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'react-router-redux/ConnectedRouter' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'react-router-redux/es/actions' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'react-router-redux/es/ConnectedRouter' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'react-router-redux/es/index' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'react-router-redux/es/middleware' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'react-router-redux/es/reducer' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'react-router-redux/es/selectors' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'react-router-redux/middleware' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'react-router-redux/reducer' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'react-router-redux/selectors' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'react-router-redux/umd/react-router-redux' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'react-router-redux/umd/react-router-redux.min' { 74 | declare module.exports: any; 75 | } 76 | 77 | // Filename aliases 78 | declare module 'react-router-redux/actions.js' { 79 | declare module.exports: $Exports<'react-router-redux/actions'>; 80 | } 81 | declare module 'react-router-redux/ConnectedRouter.js' { 82 | declare module.exports: $Exports<'react-router-redux/ConnectedRouter'>; 83 | } 84 | declare module 'react-router-redux/es/actions.js' { 85 | declare module.exports: $Exports<'react-router-redux/es/actions'>; 86 | } 87 | declare module 'react-router-redux/es/ConnectedRouter.js' { 88 | declare module.exports: $Exports<'react-router-redux/es/ConnectedRouter'>; 89 | } 90 | declare module 'react-router-redux/es/index.js' { 91 | declare module.exports: $Exports<'react-router-redux/es/index'>; 92 | } 93 | declare module 'react-router-redux/es/middleware.js' { 94 | declare module.exports: $Exports<'react-router-redux/es/middleware'>; 95 | } 96 | declare module 'react-router-redux/es/reducer.js' { 97 | declare module.exports: $Exports<'react-router-redux/es/reducer'>; 98 | } 99 | declare module 'react-router-redux/es/selectors.js' { 100 | declare module.exports: $Exports<'react-router-redux/es/selectors'>; 101 | } 102 | declare module 'react-router-redux/index' { 103 | declare module.exports: $Exports<'react-router-redux'>; 104 | } 105 | declare module 'react-router-redux/index.js' { 106 | declare module.exports: $Exports<'react-router-redux'>; 107 | } 108 | declare module 'react-router-redux/middleware.js' { 109 | declare module.exports: $Exports<'react-router-redux/middleware'>; 110 | } 111 | declare module 'react-router-redux/reducer.js' { 112 | declare module.exports: $Exports<'react-router-redux/reducer'>; 113 | } 114 | declare module 'react-router-redux/selectors.js' { 115 | declare module.exports: $Exports<'react-router-redux/selectors'>; 116 | } 117 | declare module 'react-router-redux/umd/react-router-redux.js' { 118 | declare module.exports: $Exports<'react-router-redux/umd/react-router-redux'>; 119 | } 120 | declare module 'react-router-redux/umd/react-router-redux.min.js' { 121 | declare module.exports: $Exports<'react-router-redux/umd/react-router-redux.min'>; 122 | } 123 | -------------------------------------------------------------------------------- /flow-typed/npm/react-scripts_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 0da9532a6d0f1ea895af9b17ce71fb89 2 | // flow-typed version: <>/react-scripts_v1.1.5/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'react-scripts' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'react-scripts' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'react-scripts/bin/react-scripts' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'react-scripts/config/env' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'react-scripts/config/jest/babelTransform' { 34 | declare module.exports: any; 35 | } 36 | 37 | declare module 'react-scripts/config/jest/cssTransform' { 38 | declare module.exports: any; 39 | } 40 | 41 | declare module 'react-scripts/config/jest/fileTransform' { 42 | declare module.exports: any; 43 | } 44 | 45 | declare module 'react-scripts/config/paths' { 46 | declare module.exports: any; 47 | } 48 | 49 | declare module 'react-scripts/config/polyfills' { 50 | declare module.exports: any; 51 | } 52 | 53 | declare module 'react-scripts/config/webpack.config.dev' { 54 | declare module.exports: any; 55 | } 56 | 57 | declare module 'react-scripts/config/webpack.config.prod' { 58 | declare module.exports: any; 59 | } 60 | 61 | declare module 'react-scripts/config/webpackDevServer.config' { 62 | declare module.exports: any; 63 | } 64 | 65 | declare module 'react-scripts/scripts/build' { 66 | declare module.exports: any; 67 | } 68 | 69 | declare module 'react-scripts/scripts/eject' { 70 | declare module.exports: any; 71 | } 72 | 73 | declare module 'react-scripts/scripts/init' { 74 | declare module.exports: any; 75 | } 76 | 77 | declare module 'react-scripts/scripts/start' { 78 | declare module.exports: any; 79 | } 80 | 81 | declare module 'react-scripts/scripts/test' { 82 | declare module.exports: any; 83 | } 84 | 85 | declare module 'react-scripts/scripts/utils/createJestConfig' { 86 | declare module.exports: any; 87 | } 88 | 89 | declare module 'react-scripts/template/src/App' { 90 | declare module.exports: any; 91 | } 92 | 93 | declare module 'react-scripts/template/src/App.test' { 94 | declare module.exports: any; 95 | } 96 | 97 | declare module 'react-scripts/template/src/index' { 98 | declare module.exports: any; 99 | } 100 | 101 | declare module 'react-scripts/template/src/registerServiceWorker' { 102 | declare module.exports: any; 103 | } 104 | 105 | // Filename aliases 106 | declare module 'react-scripts/bin/react-scripts.js' { 107 | declare module.exports: $Exports<'react-scripts/bin/react-scripts'>; 108 | } 109 | declare module 'react-scripts/config/env.js' { 110 | declare module.exports: $Exports<'react-scripts/config/env'>; 111 | } 112 | declare module 'react-scripts/config/jest/babelTransform.js' { 113 | declare module.exports: $Exports<'react-scripts/config/jest/babelTransform'>; 114 | } 115 | declare module 'react-scripts/config/jest/cssTransform.js' { 116 | declare module.exports: $Exports<'react-scripts/config/jest/cssTransform'>; 117 | } 118 | declare module 'react-scripts/config/jest/fileTransform.js' { 119 | declare module.exports: $Exports<'react-scripts/config/jest/fileTransform'>; 120 | } 121 | declare module 'react-scripts/config/paths.js' { 122 | declare module.exports: $Exports<'react-scripts/config/paths'>; 123 | } 124 | declare module 'react-scripts/config/polyfills.js' { 125 | declare module.exports: $Exports<'react-scripts/config/polyfills'>; 126 | } 127 | declare module 'react-scripts/config/webpack.config.dev.js' { 128 | declare module.exports: $Exports<'react-scripts/config/webpack.config.dev'>; 129 | } 130 | declare module 'react-scripts/config/webpack.config.prod.js' { 131 | declare module.exports: $Exports<'react-scripts/config/webpack.config.prod'>; 132 | } 133 | declare module 'react-scripts/config/webpackDevServer.config.js' { 134 | declare module.exports: $Exports<'react-scripts/config/webpackDevServer.config'>; 135 | } 136 | declare module 'react-scripts/scripts/build.js' { 137 | declare module.exports: $Exports<'react-scripts/scripts/build'>; 138 | } 139 | declare module 'react-scripts/scripts/eject.js' { 140 | declare module.exports: $Exports<'react-scripts/scripts/eject'>; 141 | } 142 | declare module 'react-scripts/scripts/init.js' { 143 | declare module.exports: $Exports<'react-scripts/scripts/init'>; 144 | } 145 | declare module 'react-scripts/scripts/start.js' { 146 | declare module.exports: $Exports<'react-scripts/scripts/start'>; 147 | } 148 | declare module 'react-scripts/scripts/test.js' { 149 | declare module.exports: $Exports<'react-scripts/scripts/test'>; 150 | } 151 | declare module 'react-scripts/scripts/utils/createJestConfig.js' { 152 | declare module.exports: $Exports<'react-scripts/scripts/utils/createJestConfig'>; 153 | } 154 | declare module 'react-scripts/template/src/App.js' { 155 | declare module.exports: $Exports<'react-scripts/template/src/App'>; 156 | } 157 | declare module 'react-scripts/template/src/App.test.js' { 158 | declare module.exports: $Exports<'react-scripts/template/src/App.test'>; 159 | } 160 | declare module 'react-scripts/template/src/index.js' { 161 | declare module.exports: $Exports<'react-scripts/template/src/index'>; 162 | } 163 | declare module 'react-scripts/template/src/registerServiceWorker.js' { 164 | declare module.exports: $Exports<'react-scripts/template/src/registerServiceWorker'>; 165 | } 166 | -------------------------------------------------------------------------------- /flow-typed/npm/react-test-renderer_v16.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 9b9f4128694a7f68659d945b81fb78ff 2 | // flow-typed version: 46dfe79a54/react-test-renderer_v16.x.x/flow_>=v0.47.x 3 | 4 | // Type definitions for react-test-renderer 16.x.x 5 | // Ported from: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-test-renderer 6 | 7 | type ReactComponentInstance = React$Component; 8 | 9 | type ReactTestRendererJSON = { 10 | type: string, 11 | props: { [propName: string]: any }, 12 | children: null | ReactTestRendererJSON[] 13 | }; 14 | 15 | type ReactTestRendererTree = ReactTestRendererJSON & { 16 | nodeType: "component" | "host", 17 | instance: ?ReactComponentInstance, 18 | rendered: null | ReactTestRendererTree 19 | }; 20 | 21 | type ReactTestInstance = { 22 | instance: ?ReactComponentInstance, 23 | type: string, 24 | props: { [propName: string]: any }, 25 | parent: null | ReactTestInstance, 26 | children: Array, 27 | 28 | find(predicate: (node: ReactTestInstance) => boolean): ReactTestInstance, 29 | findByType(type: React$ElementType): ReactTestInstance, 30 | findByProps(props: { [propName: string]: any }): ReactTestInstance, 31 | 32 | findAll( 33 | predicate: (node: ReactTestInstance) => boolean, 34 | options?: { deep: boolean } 35 | ): ReactTestInstance[], 36 | findAllByType( 37 | type: React$ElementType, 38 | options?: { deep: boolean } 39 | ): ReactTestInstance[], 40 | findAllByProps( 41 | props: { [propName: string]: any }, 42 | options?: { deep: boolean } 43 | ): ReactTestInstance[] 44 | }; 45 | 46 | type TestRendererOptions = { 47 | createNodeMock(element: React$Element): any 48 | }; 49 | 50 | declare module "react-test-renderer" { 51 | declare export type ReactTestRenderer = { 52 | toJSON(): null | ReactTestRendererJSON, 53 | toTree(): null | ReactTestRendererTree, 54 | unmount(nextElement?: React$Element): void, 55 | update(nextElement: React$Element): void, 56 | getInstance(): ?ReactComponentInstance, 57 | root: ReactTestInstance 58 | }; 59 | 60 | declare function create( 61 | nextElement: React$Element, 62 | options?: TestRendererOptions 63 | ): ReactTestRenderer; 64 | } 65 | 66 | declare module "react-test-renderer/shallow" { 67 | declare export default class ShallowRenderer { 68 | static createRenderer(): ShallowRenderer; 69 | getMountedInstance(): ReactTestInstance; 70 | getRenderOutput>(): E; 71 | getRenderOutput(): React$Element; 72 | render(element: React$Element, context?: any): void; 73 | unmount(): void; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /flow-typed/npm/redux-devtools-extension_v2.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 263123e4b3d2cb666a60f721c2da5354 2 | // flow-typed version: e1af06321a/redux-devtools-extension_v2.x.x/flow_>=v0.47.x 3 | 4 | import type { ActionCreator, StoreEnhancer } from 'redux'; 5 | import typeof { compose } from 'redux'; 6 | 7 | declare type $npm$ReduxDevtoolsExtension$DevToolsOptions = { 8 | name?: string, 9 | actionCreators?: Array> | { [string]: ActionCreator }, 10 | latency?: number, 11 | maxAge?: number, 12 | serialize?: boolean | { 13 | date?: boolean; 14 | regex?: boolean; 15 | undefined?: boolean; 16 | error?: boolean; 17 | symbol?: boolean; 18 | map?: boolean; 19 | set?: boolean; 20 | function?: boolean | Function; 21 | }, 22 | actionSanitizer?: }>(action: A, id: number) => A, 23 | stateSanitizer?: (state: S, index: number) => S, 24 | actionsBlacklist?: string | string[], 25 | actionsWhitelist?: string | string[], 26 | predicate?: }>(state: S, action: A) => boolean, 27 | shouldRecordChanges?: boolean, 28 | pauseActionType?: string, 29 | autoPause?: boolean, 30 | shouldStartLocked?: boolean, 31 | shouldHotReload?: boolean, 32 | shouldCatchErrors?: boolean, 33 | features?: { 34 | pause?: boolean, 35 | lock?: boolean, 36 | persist?: boolean, 37 | export?: boolean | "custom", 38 | import?: boolean | "custom", 39 | jump?: boolean, 40 | skip?: boolean, 41 | reorder?: boolean, 42 | dispatch?: boolean, 43 | test?: boolean 44 | } 45 | }; 46 | 47 | declare function $npm$ReduxDevtoolsExtension$composeWithDevTools(ab: A => B): A => B; 48 | declare function $npm$ReduxDevtoolsExtension$composeWithDevTools(options: $npm$ReduxDevtoolsExtension$DevToolsOptions): compose; 49 | declare function $npm$ReduxDevtoolsExtension$composeWithDevTools( 50 | bc: B => C, 51 | ab: A => B 52 | ): A => C; 53 | declare function $npm$ReduxDevtoolsExtension$composeWithDevTools( 54 | cd: C => D, 55 | bc: B => C, 56 | ab: A => B 57 | ): A => D; 58 | declare function $npm$ReduxDevtoolsExtension$composeWithDevTools( 59 | de: D => E, 60 | cd: C => D, 61 | bc: B => C, 62 | ab: A => B 63 | ): A => E; 64 | declare function $npm$ReduxDevtoolsExtension$composeWithDevTools( 65 | ef: E => F, 66 | de: D => E, 67 | cd: C => D, 68 | bc: B => C, 69 | ab: A => B 70 | ): A => F; 71 | declare function $npm$ReduxDevtoolsExtension$composeWithDevTools( 72 | fg: F => G, 73 | ef: E => F, 74 | de: D => E, 75 | cd: C => D, 76 | bc: B => C, 77 | ab: A => B 78 | ): A => G; 79 | declare function $npm$ReduxDevtoolsExtension$composeWithDevTools( 80 | gh: G => H, 81 | fg: F => G, 82 | ef: E => F, 83 | de: D => E, 84 | cd: C => D, 85 | bc: B => C, 86 | ab: A => B 87 | ): A => H; 88 | declare function $npm$ReduxDevtoolsExtension$composeWithDevTools( 89 | hi: H => I, 90 | gh: G => H, 91 | fg: F => G, 92 | ef: E => F, 93 | de: D => E, 94 | cd: C => D, 95 | bc: B => C, 96 | ab: A => B 97 | ): A => H; 98 | 99 | declare function $npm$ReduxDevtoolsExtension$devToolsEnhancer(options?: $npm$ReduxDevtoolsExtension$DevToolsOptions): StoreEnhancer; 100 | 101 | declare module 'redux-devtools-extension' { 102 | declare export type DevToolsOptions = $npm$ReduxDevtoolsExtension$DevToolsOptions; 103 | 104 | declare export var composeWithDevTools: typeof $npm$ReduxDevtoolsExtension$composeWithDevTools; 105 | declare export var devToolsEnhancer: typeof $npm$ReduxDevtoolsExtension$devToolsEnhancer; 106 | } 107 | 108 | declare module 'redux-devtools-extension/developmentOnly' { 109 | declare export type DevToolsOptions = $npm$ReduxDevtoolsExtension$DevToolsOptions; 110 | 111 | declare export var composeWithDevTools: typeof $npm$ReduxDevtoolsExtension$composeWithDevTools; 112 | declare export var devToolsEnhancer: typeof $npm$ReduxDevtoolsExtension$devToolsEnhancer; 113 | } 114 | 115 | declare module 'redux-devtools-extension/logOnly' { 116 | declare export type DevToolsOptions = $npm$ReduxDevtoolsExtension$DevToolsOptions; 117 | 118 | declare export var composeWithDevTools: typeof $npm$ReduxDevtoolsExtension$composeWithDevTools; 119 | declare export var devToolsEnhancer: typeof $npm$ReduxDevtoolsExtension$devToolsEnhancer; 120 | } 121 | 122 | declare module 'redux-devtools-extension/logOnlyInProduction' { 123 | declare export type DevToolsOptions = $npm$ReduxDevtoolsExtension$DevToolsOptions; 124 | 125 | declare export var composeWithDevTools: typeof $npm$ReduxDevtoolsExtension$composeWithDevTools; 126 | declare export var devToolsEnhancer: typeof $npm$ReduxDevtoolsExtension$devToolsEnhancer; 127 | } 128 | -------------------------------------------------------------------------------- /flow-typed/npm/redux-mock-store_v1.2.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 1a52c9b15ddf789276c0cf9a8a0f3802 2 | // flow-typed version: e3d371228f/redux-mock-store_v1.2.x/flow_>=v0.25.x 3 | 4 | declare module "redux-mock-store" { 5 | /* 6 | S = State 7 | A = Action 8 | */ 9 | 10 | declare type mockStore = { 11 | (state: S): mockStoreWithoutMiddleware 12 | }; 13 | declare type DispatchAPI = (action: A) => A; 14 | declare type Dispatch }> = DispatchAPI; 15 | declare type mockStoreWithoutMiddleware = { 16 | getState(): S, 17 | getActions(): Array, 18 | dispatch: Dispatch, 19 | clearActions(): void, 20 | subscribe(callback: Function): () => void, 21 | replaceReducer(nextReducer: Function): void 22 | }; 23 | 24 | declare module.exports: (middlewares: ?Array) => mockStore; 25 | } 26 | 27 | // Filename aliases 28 | declare module "redux-mock-store/src/index" { 29 | declare module.exports: $Exports<"redux-mock-store">; 30 | } 31 | declare module "redux-mock-store/src/index.js" { 32 | declare module.exports: $Exports<"redux-mock-store">; 33 | } 34 | -------------------------------------------------------------------------------- /flow-typed/npm/redux_v3.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: cca4916b0213065533df8335c3285a4a 2 | // flow-typed version: cab04034e7/redux_v3.x.x/flow_>=v0.55.x 3 | 4 | declare module 'redux' { 5 | 6 | /* 7 | 8 | S = State 9 | A = Action 10 | D = Dispatch 11 | 12 | */ 13 | 14 | declare export type DispatchAPI = (action: A) => A; 15 | declare export type Dispatch }> = DispatchAPI; 16 | 17 | declare export type MiddlewareAPI> = { 18 | dispatch: D; 19 | getState(): S; 20 | }; 21 | 22 | declare export type Store> = { 23 | // rewrite MiddlewareAPI members in order to get nicer error messages (intersections produce long messages) 24 | dispatch: D; 25 | getState(): S; 26 | subscribe(listener: () => void): () => void; 27 | replaceReducer(nextReducer: Reducer): void 28 | }; 29 | 30 | declare export type Reducer = (state: S | void, action: A) => S; 31 | 32 | declare export type CombinedReducer = (state: $Shape & {} | void, action: A) => S; 33 | 34 | declare export type Middleware> = 35 | (api: MiddlewareAPI) => 36 | (next: D) => D; 37 | 38 | declare export type StoreCreator> = { 39 | (reducer: Reducer, enhancer?: StoreEnhancer): Store; 40 | (reducer: Reducer, preloadedState: S, enhancer?: StoreEnhancer): Store; 41 | }; 42 | 43 | declare export type StoreEnhancer> = (next: StoreCreator) => StoreCreator; 44 | 45 | declare export function createStore(reducer: Reducer, enhancer?: StoreEnhancer): Store; 46 | declare export function createStore(reducer: Reducer, preloadedState?: S, enhancer?: StoreEnhancer): Store; 47 | 48 | declare export function applyMiddleware(...middlewares: Array>): StoreEnhancer; 49 | 50 | declare export type ActionCreator = (...args: Array) => A; 51 | declare export type ActionCreators = { [key: K]: ActionCreator }; 52 | 53 | declare export function bindActionCreators, D: DispatchAPI>(actionCreator: C, dispatch: D): C; 54 | declare export function bindActionCreators, D: DispatchAPI>(actionCreators: C, dispatch: D): C; 55 | 56 | declare export function combineReducers(reducers: O): CombinedReducer<$ObjMap(r: Reducer) => S>, A>; 57 | 58 | declare export var compose: $Compose; 59 | } 60 | -------------------------------------------------------------------------------- /flow-typed/npm/url-search-params-polyfill_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 8aa1ad5efb7fe8237de86f525f39f3bf 2 | // flow-typed version: <>/url-search-params-polyfill_v^2.0.0/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'url-search-params-polyfill' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'url-search-params-polyfill' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'url-search-params-polyfill/test/index.test' { 26 | declare module.exports: any; 27 | } 28 | 29 | declare module 'url-search-params-polyfill/test/partial-support.test' { 30 | declare module.exports: any; 31 | } 32 | 33 | declare module 'url-search-params-polyfill/test/USP-without-full-feature' { 34 | declare module.exports: any; 35 | } 36 | 37 | // Filename aliases 38 | declare module 'url-search-params-polyfill/index' { 39 | declare module.exports: $Exports<'url-search-params-polyfill'>; 40 | } 41 | declare module 'url-search-params-polyfill/index.js' { 42 | declare module.exports: $Exports<'url-search-params-polyfill'>; 43 | } 44 | declare module 'url-search-params-polyfill/test/index.test.js' { 45 | declare module.exports: $Exports<'url-search-params-polyfill/test/index.test'>; 46 | } 47 | declare module 'url-search-params-polyfill/test/partial-support.test.js' { 48 | declare module.exports: $Exports<'url-search-params-polyfill/test/partial-support.test'>; 49 | } 50 | declare module 'url-search-params-polyfill/test/USP-without-full-feature.js' { 51 | declare module.exports: $Exports<'url-search-params-polyfill/test/USP-without-full-feature'>; 52 | } 53 | -------------------------------------------------------------------------------- /flow-typed/npm/whatwg-fetch_vx.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: aa3e606a72f02ddd678444945197c684 2 | // flow-typed version: <>/whatwg-fetch_v^2.0.3/flow_v0.63.1 3 | 4 | /** 5 | * This is an autogenerated libdef stub for: 6 | * 7 | * 'whatwg-fetch' 8 | * 9 | * Fill this stub out by replacing all the `any` types. 10 | * 11 | * Once filled out, we encourage you to share your work with the 12 | * community by sending a pull request to: 13 | * https://github.com/flowtype/flow-typed 14 | */ 15 | 16 | declare module 'whatwg-fetch' { 17 | declare module.exports: any; 18 | } 19 | 20 | /** 21 | * We include stubs for each file inside this npm package in case you need to 22 | * require those files directly. Feel free to delete any files that aren't 23 | * needed. 24 | */ 25 | declare module 'whatwg-fetch/fetch' { 26 | declare module.exports: any; 27 | } 28 | 29 | // Filename aliases 30 | declare module 'whatwg-fetch/fetch.js' { 31 | declare module.exports: $Exports<'whatwg-fetch/fetch'>; 32 | } 33 | -------------------------------------------------------------------------------- /images/add-image-to-collections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/reactjs-unsplash/5ada42e802e108f93086e3a7e40f47828877d787/images/add-image-to-collections.png -------------------------------------------------------------------------------- /images/create-new-collection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/reactjs-unsplash/5ada42e802e108f93086e3a7e40f47828877d787/images/create-new-collection.png -------------------------------------------------------------------------------- /images/home-mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/reactjs-unsplash/5ada42e802e108f93086e3a7e40f47828877d787/images/home-mobile.png -------------------------------------------------------------------------------- /images/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/reactjs-unsplash/5ada42e802e108f93086e3a7e40f47828877d787/images/home.png -------------------------------------------------------------------------------- /images/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/reactjs-unsplash/5ada42e802e108f93086e3a7e40f47828877d787/images/search.png -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "verbose": true, 3 | "testURL": "http://localhost/" 4 | }; -------------------------------------------------------------------------------- /libdefs.js: -------------------------------------------------------------------------------- 1 | declare module 'polished' { declare module.exports: any; } 2 | declare module 'camelcase-keys-deep' { declare module.exports: any; } 3 | declare module 'redux-saga/effects' { declare module.exports: any; } 4 | declare module 'redux-persist' { declare module.exports: any; } 5 | declare module 'react-responsive-masonry' { declare module.exports: any; } 6 | declare module 'redux-form' { declare module.exports: any; } 7 | declare module 'history' { declare module.exports: any; } 8 | declare module 'react-redux' { declare module.exports: any; } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactjs-unsplash", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/atahani/reactjs-unsplash" 8 | }, 9 | "scripts": { 10 | "start": "react-scripts start", 11 | "build": "react-scripts build", 12 | "test": "react-scripts test --env=jsdom", 13 | "eject": "react-scripts eject", 14 | "precommit": "lint-staged", 15 | "lint": "eslint ./src", 16 | "flow": "flow check", 17 | "update-flowtyped": "flow-typed update" 18 | }, 19 | "author": "Ahmad Tahani ", 20 | "license": "MIT", 21 | "devDependencies": { 22 | "@types/jest": "^22.0.1", 23 | "eslint-cli": "^1.1.1", 24 | "eslint-config-react-app": "^2.1.0", 25 | "eslint-plugin-flowtype": "^2.50.3", 26 | "eslint-plugin-jsx-a11y": "^5.1.1", 27 | "file-loader": "^1.1.6", 28 | "flow-bin": "^0.63.1", 29 | "husky": "^1.3.1", 30 | "lint-staged": "^7.3.0", 31 | "prettier": "1.14.3", 32 | "react-hot-loader": "4.0.0-beta.14", 33 | "react-test-renderer": "^16.7.0", 34 | "redux-devtools-extension": "^2.13.7", 35 | "redux-mock-store": "^1.2.3" 36 | }, 37 | "dependencies": { 38 | "camelcase-keys-deep": "^0.1.0", 39 | "history": "^4.6.3", 40 | "jest-styled-components": "^6.3.1", 41 | "lodash": "^4.17.11", 42 | "polished": "^1.2.1", 43 | "react": "^16.7.0", 44 | "react-dom": "^16.7.0", 45 | "react-event-listener": "^0.5.3", 46 | "react-helmet": "^5.1.3", 47 | "react-onclickoutside": "^6.1.1", 48 | "react-redux": "^5.1.1", 49 | "react-responsive-masonry": "^2.0.0", 50 | "react-router-dom": "^4.1.1", 51 | "react-router-redux": "next", 52 | "react-scripts": "^2.1.3", 53 | "redux": "^3.6.0", 54 | "redux-form": "^7.2.0", 55 | "redux-persist": "^5.4.0", 56 | "redux-saga": "^0.16.2", 57 | "styled-components": "^3.4.10", 58 | "url-search-params-polyfill": "^2.0.0", 59 | "whatwg-fetch": "^2.0.3" 60 | }, 61 | "browserslist": [ 62 | ">0.2%", 63 | "not dead", 64 | "not ie <= 11", 65 | "not op_mini all" 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | unsplash clone 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |

20 | 21 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "unsplash clone", 3 | "name": "unsplash clone - written only for educational purpose", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /public/statics/apple-touch-icon-114x114-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/reactjs-unsplash/5ada42e802e108f93086e3a7e40f47828877d787/public/statics/apple-touch-icon-114x114-precomposed.png -------------------------------------------------------------------------------- /public/statics/apple-touch-icon-120x120-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/reactjs-unsplash/5ada42e802e108f93086e3a7e40f47828877d787/public/statics/apple-touch-icon-120x120-precomposed.png -------------------------------------------------------------------------------- /public/statics/apple-touch-icon-144x144-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/reactjs-unsplash/5ada42e802e108f93086e3a7e40f47828877d787/public/statics/apple-touch-icon-144x144-precomposed.png -------------------------------------------------------------------------------- /public/statics/apple-touch-icon-152x152-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/reactjs-unsplash/5ada42e802e108f93086e3a7e40f47828877d787/public/statics/apple-touch-icon-152x152-precomposed.png -------------------------------------------------------------------------------- /public/statics/apple-touch-icon-60x60-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/reactjs-unsplash/5ada42e802e108f93086e3a7e40f47828877d787/public/statics/apple-touch-icon-60x60-precomposed.png -------------------------------------------------------------------------------- /public/statics/apple-touch-icon-72x72-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/reactjs-unsplash/5ada42e802e108f93086e3a7e40f47828877d787/public/statics/apple-touch-icon-72x72-precomposed.png -------------------------------------------------------------------------------- /public/statics/apple-touch-icon-76x76-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/reactjs-unsplash/5ada42e802e108f93086e3a7e40f47828877d787/public/statics/apple-touch-icon-76x76-precomposed.png -------------------------------------------------------------------------------- /public/statics/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/reactjs-unsplash/5ada42e802e108f93086e3a7e40f47828877d787/public/statics/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/statics/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #FFFFFF 10 | 11 | 12 | -------------------------------------------------------------------------------- /public/statics/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/reactjs-unsplash/5ada42e802e108f93086e3a7e40f47828877d787/public/statics/favicon.ico -------------------------------------------------------------------------------- /src/actions/app.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { Action } from '../types'; 4 | 5 | /** 6 | * set last path_name 7 | * used in router/LOCATION_CHANGE action middleware 8 | * @param {string} pathName 9 | */ 10 | export const setLastPathName = (pathName: string): Action => ({ 11 | type: 'app/SE_LAST_PATH_NAME', 12 | pathName, 13 | }); 14 | 15 | /** 16 | * clear the store 17 | * used in LOGOUT 18 | */ 19 | export const clearStore = (): Action => ({ type: 'app/CL_STORE' }); 20 | 21 | /** 22 | * change job status for aync actions like get data from server 23 | * it's used for main progress bar in app 24 | * @param {bool} status 25 | */ 26 | export const jobStatus = (status: boolean = false): Action => ({ 27 | type: 'app/CH_JOB_ST', 28 | status, 29 | }); 30 | 31 | /** 32 | * custom action data with actionType 33 | * used for single action data like error or data in async actions 34 | * @param {string} actionType 35 | * @param {any} data 36 | */ 37 | export const setActionData = (actionType: string, data: ?any): Action => ({ 38 | type: 'app/SE_ACTION_DATA', 39 | actionType, 40 | data, 41 | }); 42 | 43 | export const setSearchValues = ( 44 | query: string = '', 45 | title: string = '', 46 | value: string = '' 47 | ): Action => ({ type: 'app/SE_SEARCH_VALUES', query, title, value }); 48 | -------------------------------------------------------------------------------- /src/actions/app.test.js: -------------------------------------------------------------------------------- 1 | import { clearStore, jobStatus, setActionData, setLastPathName } from './app'; 2 | 3 | describe('app actions', () => { 4 | describe('clearStore', () => { 5 | it('should have a type of "CL_STORE"', () => { 6 | expect(clearStore().type).toEqual('app/CL_STORE'); 7 | }); 8 | }); 9 | describe('jobStatus', () => { 10 | it('should have a type of "CH_JOB_ST"', () => { 11 | expect(jobStatus().type).toEqual('app/CH_JOB_ST'); 12 | }); 13 | it('should have status payload', () => { 14 | expect(jobStatus(true).status).toEqual(true); 15 | }); 16 | }); 17 | describe('setActionData', () => { 18 | it('should have a type of "SE_ACTION_DATA"', () => { 19 | expect(setActionData().type).toEqual('app/SE_ACTION_DATA'); 20 | }); 21 | it('should have actionType payload', () => { 22 | const actionType = 'ge_collection'; 23 | expect(setActionData(actionType).actionType).toEqual(actionType); 24 | expect(setActionData(actionType).actionType).not.toBeNull(); 25 | }); 26 | it('should have data payload, can be any type', () => { 27 | const collection = { 28 | id: '123', 29 | name: 'Nice', 30 | }; 31 | expect(setActionData('ge_collection', collection).data).toMatchObject( 32 | collection 33 | ); 34 | }); 35 | }); 36 | describe('setLastPathName', () => { 37 | it('it should have a type of "SE_LAST_PATH_NAME"', () => { 38 | expect(setLastPathName('/').type).toEqual('app/SE_LAST_PATH_NAME'); 39 | }); 40 | it('it should have pathName', () => { 41 | expect(setLastPathName('/auth').pathName).toEqual('/auth'); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/actions/collection.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { Action } from '../types'; 4 | import type { Collection } from '../types/data'; 5 | 6 | /** 7 | * get user collections 8 | * @param {string} url the url of request 9 | */ 10 | export const getUserCollections = (url: string): Action => ({ 11 | type: 'collection/GE_USER_COLLECTIONS', 12 | url, 13 | }); 14 | 15 | /** 16 | * get one collection 17 | * @param {string} url url request 18 | */ 19 | export const getCollection = ( 20 | url: string, 21 | loadPhotos: boolean = false 22 | ): Action => ({ type: 'collection/GE_COLLECTION', url, loadPhotos }); 23 | 24 | /** 25 | * get photos of collection 26 | * @param {string} url url request 27 | */ 28 | export const getCollectionPhotos = (url: string): Action => ({ 29 | type: 'collection/GE_COLLECTION_PHOTOS', 30 | url, 31 | }); 32 | 33 | /** 34 | * search in collections 35 | * @param {string} url url for search request 36 | */ 37 | export const searchInCollections = (url: string): Action => ({ 38 | type: 'collection/SEARCH_COLLECTIONS', 39 | url, 40 | }); 41 | 42 | /** 43 | * create collection 44 | * @param {collection} collection object 45 | */ 46 | export const createCollection = (collection: Collection): Action => ({ 47 | type: 'collection/CREATE_COLLECTION', 48 | collection, 49 | }); 50 | 51 | /** 52 | * update collection 53 | * @param {Collection} collection object of collection from form 54 | */ 55 | export const updateCollection = (collection: Collection): Action => ({ 56 | type: 'collection/UPDATE_COLLECTION', 57 | collection, 58 | }); 59 | 60 | /** 61 | * delete collection 62 | * @param {string} id collection id 63 | */ 64 | export const deleteCollection = (id: string): Action => ({ 65 | type: 'collection/DELETE_COLLECTION', 66 | id, 67 | }); 68 | 69 | /** 70 | * add photo to collection 71 | * @param {number} collectionId collection id 72 | * @param {string} photoId photo id 73 | */ 74 | export const addPhotoToCollection = ( 75 | collectionId: string, 76 | photoId: string 77 | ): Action => ({ 78 | type: 'collection/ADD_PHOTO_TO_COLLECTION', 79 | collectionId, 80 | photoId, 81 | }); 82 | 83 | /** 84 | * remove photo from collection 85 | * @param {number} collectionId collection id 86 | * @param {string} photoId photo id 87 | */ 88 | export const removePhotoFromCollection = ( 89 | collectionId: string, 90 | photoId: string 91 | ): Action => ({ 92 | type: 'collection/REMOVE_PHOTO_FROM_COLLECTION', 93 | collectionId, 94 | photoId, 95 | }); 96 | -------------------------------------------------------------------------------- /src/actions/items.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { Action } from '../types'; 4 | 5 | /** 6 | * set array of items like 7 | * [ 8 | * {id:"192837", ...otherItemsFields}, 9 | * {id:"121230", ...otherItemsFields}, 10 | * ] 11 | * @param {array} payload array of items 12 | * @param {string} entity entity like cards 13 | */ 14 | export const setItems = (entity: string, payload: Array): Action => ({ 15 | type: 'items/SE_ITEMS', 16 | entity, 17 | payload, 18 | }); 19 | 20 | /** 21 | * update item 22 | * payload is updated item object 23 | * @param {object} payload updated item object 24 | * @param {string} entity entity like cards 25 | */ 26 | export const updateItem = (entity: string, payload: Object): Action => ({ 27 | type: 'items/UP_ITEM', 28 | entity, 29 | payload, 30 | }); 31 | 32 | /** 33 | * remove item with item id 34 | * payload is item id 35 | * @param {string} payload item id 36 | * @param {string} entity entity like cards 37 | */ 38 | export const removeItem = (entity: string, id: string): Action => ({ 39 | type: 'items/RM_ITEM', 40 | entity, 41 | id, 42 | }); 43 | 44 | /** 45 | * clear items remove all of the items in entity 46 | * used in unMount component event 47 | * @param {string} entity entity like cards 48 | */ 49 | export const clearItems = (entity: string): Action => ({ 50 | type: 'items/CL_ITEMS', 51 | entity, 52 | }); 53 | 54 | /** 55 | * set items attr like total or links 56 | * @param {string} entity entity like cards 57 | * @param {object} attrObj object of attr like { total:20 } 58 | */ 59 | export const setItemsAttr = (entity: string, attrObj: ?Object): Action => ({ 60 | type: 'items/SE_ITEMS_ATTR', 61 | entity, 62 | attrObj, 63 | }); 64 | 65 | /** 66 | * update fields of item 67 | * @param {string} entity entity like 'liked-photos' 68 | * @param {*} id the id of itme 69 | * @param {*} fields updated fields { likes: 123, likedByUser: false } 70 | */ 71 | export const updateFieldsOfItem = ( 72 | entity: string, 73 | id: string, 74 | fields: ?Object 75 | ): Action => ({ type: 'items/UP_FIELD_OF_ITEM', entity, id, fields }); 76 | 77 | /** 78 | * set one object in entity 79 | * @param {string} entity entity like 'liked-photos' 80 | * @param {object} payload one item object 81 | */ 82 | export const setItem = (entity: string, payload: Object): Action => ({ 83 | type: 'items/SE_ITEM', 84 | entity, 85 | payload, 86 | }); 87 | -------------------------------------------------------------------------------- /src/actions/photo.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { Action } from '../types'; 4 | 5 | /** 6 | * url request for photos 7 | * @param {string} url request url for get photos 8 | */ 9 | export const getPhotos = (url: string): Action => ({ 10 | type: 'photo/GE_PHOTOS', 11 | url, 12 | }); 13 | 14 | /** 15 | * like photo via id 16 | * @param {string} id photo id 17 | */ 18 | export const likePhoto = (id: string): Action => ({ 19 | type: 'photo/LIKE_PHOTO', 20 | id, 21 | }); 22 | 23 | /** 24 | * unlike photo via id 25 | * @param {string} id photo id 26 | */ 27 | export const unLikePhoto = (id: string): Action => ({ 28 | type: 'photo/UNLIKE_PHOTO', 29 | id, 30 | }); 31 | 32 | /** 33 | * search in photos 34 | * @param {string} url request url for search in photos 35 | */ 36 | export const searchInPhotos = (url: string): Action => ({ 37 | type: 'photo/SEARCH_PHOTOS', 38 | url, 39 | }); 40 | 41 | /** 42 | * get photo by id 43 | * @param {strin} id get photo 44 | */ 45 | export const getPhoto = (id: string): Action => ({ 46 | type: 'photo/GE_PHOTO', 47 | id, 48 | }); 49 | -------------------------------------------------------------------------------- /src/actions/user.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type { Action } from '../types'; 4 | import type { AuthorizeToken, UserProfile } from '../types/data'; 5 | 6 | /** 7 | * get access token with authorization code 8 | * @param {string} code string 9 | */ 10 | export const getAccessToken = (code: string): Action => ({ 11 | type: 'user/GE_ACCESS_TOKEN', 12 | code, 13 | }); 14 | 15 | /** 16 | * set access_token and other field 17 | * @param {AuthorizeToken} AuthorizeToken 18 | */ 19 | export const setAccessToken = (payload: AuthorizeToken): Action => ({ 20 | type: 'user/SE_ACCESS_TOKEN', 21 | payload, 22 | }); 23 | 24 | /** 25 | * logout user 26 | */ 27 | export const logout = (): Action => ({ type: 'user/LOGOUT' }); 28 | 29 | /** 30 | * get logged in user profile 31 | */ 32 | export const getProfile = (): Action => ({ type: 'user/GE_USER_PROFILE' }); 33 | 34 | /** 35 | * set logged in user profile 36 | * @param {object} payload 37 | */ 38 | export const setProfile = (payload: UserProfile): Action => ({ 39 | type: 'user/SE_USER_PROFILE', 40 | payload, 41 | }); 42 | -------------------------------------------------------------------------------- /src/api/api-error.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | export default class APIError extends Error { 4 | code: number; 5 | errors: string; 6 | } 7 | -------------------------------------------------------------------------------- /src/api/collection.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import { 4 | postReq, 5 | putReq, 6 | deleteReqWithoutJSON, 7 | deleteReq, 8 | } from './rest-helper'; 9 | import { API_ROOT } from '../constants/service-info'; 10 | import type { Collection, RESTAPIResponse } from '../types/data'; 11 | 12 | /** 13 | * create collection request 14 | * @param {collection} new collection object 15 | */ 16 | export const createCollection = (collection: Collection): RESTAPIResponse => 17 | postReq(`${API_ROOT}/collections`, { 18 | title: collection.title, 19 | description: collection.description, 20 | private: collection.private, 21 | }); 22 | 23 | /** 24 | * update collection request 25 | * @param {string} id collection id 26 | * @param {collection} collection object 27 | */ 28 | export const updateCollection = (collection: Collection): RESTAPIResponse => 29 | putReq(`${API_ROOT}/collections/${collection.id ? collection.id : ''}`, { 30 | title: collection.title, 31 | description: collection.description, 32 | private: collection.private, 33 | }); 34 | 35 | /** 36 | * delete collection by id 37 | * @param {string} id collection id 38 | */ 39 | export const deleteCollection = (id: string): RESTAPIResponse => 40 | deleteReqWithoutJSON(`${API_ROOT}/collections/${id}`); 41 | 42 | /** 43 | * add photo to collection 44 | * @param {string} photoId photo id 45 | * @param {number} collectionId collection id 46 | */ 47 | export const addPhotoToCollection = ( 48 | photoId: string, 49 | collectionId: string 50 | ): RESTAPIResponse => 51 | postReq(`${API_ROOT}/collections/${collectionId}/add`, { photo_id: photoId }); 52 | 53 | /** 54 | * remove photo from collection 55 | * @param {string} photoId photo id 56 | * @param {number} collectionId collection id 57 | */ 58 | export const removePhotoFromCollection = ( 59 | photoId: string, 60 | collectionId: string 61 | ): RESTAPIResponse => 62 | deleteReq(`${API_ROOT}/collections/${collectionId}/remove`, { 63 | photo_id: photoId, 64 | }); 65 | -------------------------------------------------------------------------------- /src/api/photo.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import type { RESTAPIResponse } from '../types/data'; 4 | import { getReq, postReq, deleteReq } from './rest-helper'; 5 | import { API_ROOT } from '../constants/service-info'; 6 | 7 | /** 8 | * get photos 9 | * @param {string} url url of get photo request 10 | */ 11 | export const getPhotos = (url: string): RESTAPIResponse => getReq(url); 12 | 13 | /** 14 | * like photo 15 | * @param {string} id photo id 16 | */ 17 | export const likePhoto = (id: string): RESTAPIResponse => 18 | postReq(`${API_ROOT}/photos/${id}/like`); 19 | 20 | /** 21 | * unlike photo 22 | * @param {string} id photo id 23 | */ 24 | export const unLikePhoto = (id: string): RESTAPIResponse => 25 | deleteReq(`${API_ROOT}/photos/${id}/like`); 26 | 27 | /** 28 | * get photo by id 29 | * @param {string} id photo it 30 | */ 31 | export const getPhoto = (id: string): RESTAPIResponse => 32 | getReq(`${API_ROOT}/photos/${id}`); 33 | -------------------------------------------------------------------------------- /src/api/rest-helper.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import type { 4 | RESTAPIResponse, 5 | ErrorResponse, 6 | SuccessResponse, 7 | } from '../types/data'; 8 | import APIError from './api-error'; 9 | import { getState } from '../store'; 10 | import { UN_AVAILABLE, UNHANDLED } from '../constants/api-error-codes'; 11 | 12 | // check status used in fetch promise 13 | function checkStatus( 14 | json: any, 15 | res: Response 16 | ): ErrorResponse | SuccessResponse { 17 | // check response in ok 200 or not 18 | if (res.ok) { 19 | // get link header from response 20 | /** 21 | * 200 OK 22 | * Link: ; rel="first", ; rel="prev", ; rel="last", ; rel="next" 23 | * X-Ratelimit-Limit: 1000 24 | * X-Ratelimit-Remaining: 999 25 | */ 26 | const attr = {}; 27 | let linkHeader = res.headers.get('link'); 28 | if (linkHeader) { 29 | linkHeader = linkHeader.replace(/[<|>|"| ]/g, '').replace(/rel=/g, ''); 30 | const links = linkHeader.split(','); 31 | links.forEach(item => { 32 | const part = item.split(';'); 33 | if (part.length === 2) { 34 | Object.defineProperty(attr, part[1].trim(), { 35 | value: part[0].trim(), 36 | writable: false, 37 | enumerable: false, 38 | }); 39 | } 40 | }); 41 | } 42 | return { response: json, attr }; 43 | } 44 | // create error with status text, message, code 45 | const error = new APIError(res.status); 46 | error.code = res.status; 47 | error.errors = json.errors; 48 | error.description = json.error_description; 49 | return { error }; 50 | } 51 | 52 | // handle failure error 53 | function failure(err: Error): ErrorResponse { 54 | // handle server unavailable error 55 | if (err.message && err.message === 'Failed to fetch') { 56 | const error = new APIError('server unavailable'); 57 | error.errors = 'server unavailable'; 58 | error.code = UN_AVAILABLE; 59 | return { error }; 60 | } else { 61 | const error = new APIError('unhandled error happend'); 62 | error.errors = 'unhandled error happend'; 63 | error.code = UNHANDLED; 64 | return { error }; 65 | } 66 | } 67 | 68 | /** 69 | * get headers of REST API request 70 | * add headers like Accept, Authorization, Content-Type 71 | * @param {bool} jsonContentType 72 | */ 73 | export const getHeaders = (jsonContentType?: boolean = true): Headers => { 74 | const headers = new Headers(); 75 | headers.append('Accept-Version', 'v1'); 76 | if (jsonContentType) { 77 | headers.append('Content-Type', 'application/json'); 78 | } 79 | headers.append('Accept', 'application/json'); 80 | if (getState().user.isAuthorized) { 81 | headers.append( 82 | 'Authorization', 83 | `Bearer ${getState().user.token.access_token}` 84 | ); 85 | } 86 | return headers; 87 | }; 88 | 89 | /** 90 | * get headers for Multi Part requests 91 | * like upload image 92 | */ 93 | export const getHeadersForMultiPart = (): Headers => { 94 | const headers = new Headers(); 95 | if (getState().user.isAuthorized) { 96 | headers.append( 97 | 'Authorization', 98 | `Bearer ${getState().user.token.access_token}` 99 | ); 100 | } 101 | return headers; 102 | }; 103 | 104 | /** 105 | * post request 106 | * @param {string} endpoint 107 | * @param {Object} body 108 | * @param {Headers} headers default is getHeaders() 109 | */ 110 | export const postReq = ( 111 | endpoint: string, 112 | body: any, 113 | headers?: Headers = getHeaders() 114 | ): RESTAPIResponse => 115 | fetch(endpoint, { 116 | method: 'POST', 117 | body: JSON.stringify(body), 118 | headers, 119 | }) 120 | .then(res => res.json().then(json => ({ json, res }))) 121 | .then(({ json, res }) => checkStatus(json, res)) 122 | .catch(failure); 123 | 124 | /** 125 | * post form for multipart requests 126 | * like upload image 127 | * @param {string} endpoint 128 | * @param {Object} formData 129 | */ 130 | export const postReqFormData = ( 131 | endpoint: string, 132 | formData: any 133 | ): RESTAPIResponse => 134 | fetch(endpoint, { 135 | method: 'POST', 136 | body: formData, 137 | headers: getHeadersForMultiPart(), 138 | }) 139 | .then(res => res.json().then(json => ({ json, res }))) 140 | .then(({ json, res }) => checkStatus(json, res)) 141 | .catch(failure); 142 | 143 | /** 144 | * get request 145 | * @param {string} endpoint 146 | * @param {Headers} headers default is getHeaders() 147 | */ 148 | export const getReq = ( 149 | endpoint: string, 150 | headers?: Headers = getHeaders() 151 | ): RESTAPIResponse => 152 | fetch(endpoint, { 153 | method: 'GET', 154 | headers, 155 | }) 156 | .then(res => res.json().then(json => ({ json, res }))) 157 | .then(({ json, res }) => checkStatus(json, res)) 158 | .catch(failure); 159 | 160 | /** 161 | * delete request 162 | * @param {string} endpoint 163 | * @param {Headers} headers default is getHeaders() 164 | */ 165 | export const deleteReq = ( 166 | endpoint: string, 167 | body: any = {}, 168 | headers?: Headers = getHeaders() 169 | ): RESTAPIResponse => 170 | fetch(endpoint, { 171 | method: 'DELETE', 172 | body: JSON.stringify(body), 173 | headers, 174 | }) 175 | .then(res => res.json().then(json => ({ json, res }))) 176 | .then(({ json, res }) => checkStatus(json, res)) 177 | .catch(failure); 178 | 179 | /** 180 | * delete request without get json in response 181 | * @param {string} endpoint 182 | * @param {Headers} headers default is getHeaders() 183 | */ 184 | export const deleteReqWithoutJSON = ( 185 | endpoint: string, 186 | headers?: Headers = getHeaders() 187 | ): RESTAPIResponse => 188 | fetch(endpoint, { 189 | method: 'DELETE', 190 | headers, 191 | }) 192 | .then(res => checkStatus({}, res)) 193 | .catch(failure); 194 | 195 | /** 196 | * put request 197 | * usually for update requests 198 | * @param {string} endpoint 199 | * @param {Object} body 200 | * @param {Headers} headers default is getHeaders() 201 | */ 202 | export const putReq = ( 203 | endpoint: string, 204 | body: any, 205 | headers?: Headers = getHeaders() 206 | ): RESTAPIResponse => 207 | fetch(endpoint, { 208 | method: 'PUT', 209 | body: JSON.stringify(body), 210 | headers, 211 | }) 212 | .then(res => res.json().then(json => ({ json, res }))) 213 | .then(({ json, res }) => checkStatus(json, res)) 214 | .catch(failure); 215 | -------------------------------------------------------------------------------- /src/api/user.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import type { RESTAPIResponse } from '../types/data'; 4 | import { postReq, getHeaders, getReq } from './rest-helper'; 5 | import { 6 | ROOT_URL, 7 | CLIENT_ID, 8 | CLIENT_SECRET, 9 | REDIRECT_URI, 10 | API_ROOT, 11 | } from '../constants/service-info'; 12 | 13 | /** 14 | * get access token from authorization code 15 | * @param {string} code authorization code that get in login action 16 | */ 17 | export const getAccessToken = (code: string): RESTAPIResponse => 18 | postReq( 19 | `${ROOT_URL}/oauth/token`, 20 | { 21 | client_id: CLIENT_ID, 22 | client_secret: CLIENT_SECRET, 23 | redirect_uri: REDIRECT_URI, 24 | code, 25 | grant_type: 'authorization_code', 26 | }, 27 | getHeaders() 28 | ); 29 | 30 | /** 31 | * get the logged in user 32 | * MORE INFO: https://unsplash.com/documentation#get-the-users-profile 33 | */ 34 | export const getUserProfile = (): RESTAPIResponse => getReq(`${API_ROOT}/me`); 35 | -------------------------------------------------------------------------------- /src/components/Avatar/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import styled from 'styled-components'; 5 | import { secondaryColor1, white } from '../../style/colors'; 6 | 7 | const Image = styled.img` 8 | width: ${props => `${props.size}px`}; 9 | height: ${props => `${props.size}px`}; 10 | border-radius: 50%; 11 | `; 12 | 13 | const NoImage = styled.div` 14 | width: ${props => `${props.size}px`}; 15 | height: ${props => `${props.size}px`}; 16 | line-height: ${props => `${props.size}px`}; 17 | font-size: ${props => `${props.size / 2}px`}; 18 | text-align: center; 19 | color: ${white}; 20 | background-color: ${secondaryColor1}; 21 | border-radius: 50%; 22 | `; 23 | 24 | type Props = { 25 | className?: string, 26 | name: string, 27 | imagePath: string, 28 | size?: number, 29 | }; 30 | 31 | const Avatar = ({ className, name, imagePath, size }: Props) => 32 | imagePath !== '' ? ( 33 | {name} 34 | ) : ( 35 | 36 | {name.substr(0, 1)} 37 | 38 | ); 39 | 40 | Avatar.defaultProps = { 41 | className: void 0, 42 | size: 40, 43 | }; 44 | 45 | export default Avatar; 46 | -------------------------------------------------------------------------------- /src/components/Button/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | //$FlowFixMe we should import Node as type but the eslint doesn't happy 4 | import React, { Node } from 'react'; 5 | import styled from 'styled-components'; 6 | import { Link } from 'react-router-dom'; 7 | import { 8 | primaryColor1, 9 | secondaryColor1, 10 | white, 11 | borderInAvtiveColor, 12 | borderAvtiveColor, 13 | } from '../../style/colors'; 14 | 15 | const Btn = styled.button` 16 | display: inline-block; 17 | margin-right: 8px; 18 | color: ${props => (props.primary ? white : secondaryColor1)}; 19 | fill: ${props => (props.primary ? props.primaryColor : secondaryColor1)}; 20 | background-color: ${props => (props.primary ? props.primaryColor : white)}; 21 | height: 32px; 22 | padding: 0 11px; 23 | font-family: inherit; 24 | font-size: 14px; 25 | font-weight: 500; 26 | line-height: 29px; 27 | text-decoration: none; 28 | cursor: pointer; 29 | border: 1px solid transparent; 30 | border-color: ${borderInAvtiveColor}; 31 | border-radius: 5px; 32 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); 33 | transition: all 0.2s ease-in-out; 34 | text-align: center; 35 | user-select: none; 36 | text-decoration: none; 37 | ${props => 38 | props.disabled 39 | ? ` 40 | opacity: 0.6; 41 | cursor: not-allowed; 42 | ` 43 | : ``} ${props => 44 | props.primary 45 | ? `` 46 | : ` 47 | &:hover, 48 | &:focus { 49 | color: ${props.primaryColor}; 50 | fill: ${props.primaryColor}; 51 | border-color: ${`${borderAvtiveColor} !important`}; 52 | } 53 | `}; 54 | `; 55 | 56 | type Props = { 57 | label?: string, 58 | onClick?: Function, 59 | children?: Node, 60 | disabled?: boolean, 61 | primary?: boolean, 62 | primaryColor?: string, 63 | type?: string, 64 | href?: string, 65 | target?: string, 66 | }; 67 | 68 | const Button = ({ 69 | disabled, 70 | label, 71 | children, 72 | primary, 73 | primaryColor, 74 | href, 75 | onClick, 76 | type, 77 | ...others 78 | }: Props) => { 79 | const handleOnClick = e => { 80 | if (onClick) { 81 | onClick(e); 82 | } 83 | }; 84 | const btn = () => ( 85 | 92 | {children ? children : label} 93 | 94 | ); 95 | const main = () => { 96 | if (href) { 97 | return ( 98 | 99 | {btn()} 100 | 101 | ); 102 | } 103 | return btn(); 104 | }; 105 | return main(); 106 | }; 107 | 108 | Button.defaultProps = { 109 | primary: false, 110 | disabled: false, 111 | primaryColor: primaryColor1, 112 | type: 'button', 113 | label: '', 114 | }; 115 | 116 | export default Button; 117 | -------------------------------------------------------------------------------- /src/components/Collection/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import { Link } from 'react-router-dom'; 5 | import styled from 'styled-components'; 6 | import { lighten } from 'polished'; 7 | import Avatar from '../Avatar'; 8 | import LockIcon from '../svg-icons/lock'; 9 | import EditIcon from '../svg-icons/edit'; 10 | import { screenLargerThan } from '../../style/util'; 11 | import { 12 | secondaryColor1, 13 | dividerColor, 14 | primaryColor1, 15 | white, 16 | } from '../../style/colors'; 17 | import type { Collection } from '../../types/data'; 18 | 19 | const Wrapper = styled.div` 20 | display: flex; 21 | max-width: 100%; 22 | flex-basis: 100%; 23 | ${screenLargerThan.tablet` 24 | flex-basis: 50%; 25 | `}; 26 | ${screenLargerThan.desktop` 27 | flex-basis: 33.3333333333%; 28 | `}; 29 | `; 30 | 31 | const Card = styled.div` 32 | width: 100%; 33 | margin: 5px; 34 | border: 1px solid ${dividerColor}; 35 | border-radius: 10px; 36 | `; 37 | 38 | const CoverLink = styled(Link)``; 39 | 40 | const Cover = styled.div` 41 | position: relative; 42 | overflow: hidden; 43 | cursor: pointer; 44 | padding-bottom: 55%; 45 | border-top-left-radius: 10px; 46 | border-top-right-radius: 10px; 47 | background-color: ${secondaryColor1}; 48 | `; 49 | 50 | const ImgCover = styled.img` 51 | display: block; 52 | position: absolute; 53 | width: 100%; 54 | height: auto; 55 | left: 50%; 56 | top: 50%; 57 | transform: translateY(-50%) translateX(-50%); 58 | `; 59 | 60 | const Overlay = styled.div` 61 | width: 100%; 62 | background-color: ${lighten(0.15, primaryColor1)}; 63 | opacity: 0.4; 64 | border-top-left-radius: 10px; 65 | border-top-right-radius: 10px; 66 | `; 67 | 68 | const Title = styled.div` 69 | font-size: 24px; 70 | font-weight: 400; 71 | position: absolute; 72 | z-index: 99; 73 | bottom: 24px; 74 | left: 35px; 75 | color: ${white}; 76 | `; 77 | 78 | const Counter = styled.div` 79 | font-size: 13px; 80 | position: absolute; 81 | z-index: 99; 82 | bottom: 10px; 83 | left: 35px; 84 | color: ${white}; 85 | opacity: 0.7; 86 | `; 87 | 88 | const PrivateIcon = styled(LockIcon)` 89 | position: absolute; 90 | bottom: 34px; 91 | left: 10px; 92 | fill: ${white}; 93 | opacity: 0.7; 94 | `; 95 | 96 | const Footer = styled.div` 97 | height: 60px; 98 | display: flex; 99 | align-items: center; 100 | padding: 0px 16px; 101 | justify-content: space-between; 102 | `; 103 | 104 | const UserLink = styled.a` 105 | display: flex; 106 | align-items: center; 107 | color: ${lighten(0.35, primaryColor1)}; 108 | `; 109 | 110 | const DisplayName = styled.div` 111 | margin-left: 8px; 112 | font-weight: 600; 113 | font-size: 16px; 114 | `; 115 | 116 | const EditBtn = styled(Link)` 117 | fill: ${secondaryColor1}; 118 | padding: 4px; 119 | cursor: pointer; 120 | `; 121 | 122 | type Props = { 123 | collection: Collection, 124 | editable: boolean, 125 | }; 126 | 127 | const CollectionCmp = ({ editable, collection, ...others }: Props) => { 128 | console.warn(collection); 129 | if (collection.id && collection.user && collection.title) { 130 | return ( 131 | 132 | 133 | 134 | 135 | 136 | {collection.title} 137 | {collection.totalPhotos !== undefined && ( 138 | {`${collection.totalPhotos} Photos`} 139 | )} 140 | {collection.coverPhoto && ( 141 | 147 | )} 148 | {collection.is_private ? : null} 149 | 150 | 151 |
152 | 153 | 157 | {collection.user.name} 158 | 159 | {editable ? ( 160 | 161 | 162 | 163 | ) : null} 164 |
165 |
166 |
167 | ); 168 | } 169 | return null; 170 | }; 171 | 172 | export default CollectionCmp; 173 | -------------------------------------------------------------------------------- /src/components/CollectionSView/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React, { Component } from 'react'; 4 | import { Link } from 'react-router-dom'; 5 | import styled from 'styled-components'; 6 | import { lighten } from 'polished'; 7 | import LockIcon from '../svg-icons/lock'; 8 | import DoneIcon from '../svg-icons/done'; 9 | import RemoveIcon from '../svg-icons/remove'; 10 | import AddIcon from '../svg-icons/add'; 11 | import { screenLargerThan } from '../../style/util'; 12 | import { 13 | dividerColor, 14 | secondaryColor1, 15 | primaryColor1, 16 | white, 17 | greenColor, 18 | } from '../../style/colors'; 19 | import type { Collection } from '../../types/data'; 20 | 21 | const Wrapper = styled.div` 22 | flex-basis: 90px; 23 | display: flex; 24 | ${props => 25 | props.inRowSelection 26 | ? ` 27 | cursor: pointer; 28 | ` 29 | : ``} ${screenLargerThan.tablet` 30 | flex-basis: 25%; 31 | `}; 32 | `; 33 | 34 | const Content = styled.div` 35 | width: 100%; 36 | margin: 8px 0px; 37 | border: 1px solid ${dividerColor}; 38 | border-radius: ${props => (props.inRowSelection ? '3px' : '10px')}; 39 | ${props => 40 | props.inRowSelection 41 | ? ` 42 | height: 120px; 43 | ` 44 | : ` 45 | height: 74px; 46 | `}; 47 | ${screenLargerThan.tablet` 48 | ${props => 49 | props.inRowSelection 50 | ? ` 51 | height: unset; 52 | margin: 0px; 53 | ` 54 | : ` 55 | height: 94px; 56 | margin: 0px 4px; 57 | `}; 58 | `}; 59 | `; 60 | 61 | const Cover = styled.div` 62 | width: 100%; 63 | height: 100%; 64 | position: relative; 65 | overflow: hidden; 66 | border-radius: ${props => (props.inRowSelection ? '3px' : '10px')}; 67 | background: ${lighten(0.1, secondaryColor1)}; 68 | `; 69 | 70 | const ImgCover = styled.img` 71 | display: block; 72 | position: absolute; 73 | width: 100%; 74 | height: auto; 75 | left: 50%; 76 | top: 50%; 77 | transform: translateY(-50%) translateX(-50%); 78 | `; 79 | 80 | const Overlay = styled.div` 81 | width: 100%; 82 | height: 100%; 83 | background-color: ${props => 84 | props.selected ? lighten(0.15, greenColor) : lighten(0.15, primaryColor1)}; 85 | opacity: 0.4; 86 | border-radius: ${props => (props.inRowSelection ? '3px' : '10px')}; 87 | `; 88 | 89 | const Title = styled.h4` 90 | font-size: 24px; 91 | font-weight: 400; 92 | position: absolute; 93 | z-index: 99; 94 | bottom: 24px; 95 | left: 35px; 96 | color: ${white}; 97 | `; 98 | 99 | const Counter = styled.div` 100 | font-size: 13px; 101 | position: absolute; 102 | z-index: 99; 103 | bottom: 10px; 104 | left: 35px; 105 | color: ${white}; 106 | opacity: 0.7; 107 | `; 108 | 109 | const PrivateIcon = styled(LockIcon)` 110 | position: absolute; 111 | bottom: 34px; 112 | left: 15px; 113 | fill: ${white}; 114 | opacity: 0.7; 115 | `; 116 | 117 | const SelectedStatusIcon = styled.div` 118 | position: absolute; 119 | right: 24px; 120 | bottom: 24px; 121 | fill: ${white}; 122 | `; 123 | 124 | type Props = { 125 | inRowSelection: boolean, 126 | selected: boolean, 127 | collection: Collection, 128 | }; 129 | 130 | type State = { 131 | overlay: boolean, 132 | }; 133 | 134 | class CollectionSView extends Component { 135 | static defaultProps = { 136 | inRowSelection: false, 137 | selected: false, 138 | }; 139 | state = { 140 | overlay: false, 141 | }; 142 | 143 | handleMouseLeave = () => { 144 | if (this.props.inRowSelection) { 145 | this.setState({ overlay: false }); 146 | } 147 | }; 148 | 149 | handleMouseEnter = () => { 150 | if (this.props.inRowSelection) { 151 | this.setState({ overlay: true }); 152 | } 153 | }; 154 | 155 | render() { 156 | const { inRowSelection, selected, collection, ...others } = this.props; 157 | const { overlay } = this.state; 158 | const selectionStatus = () => { 159 | if (selected && overlay) { 160 | return ; 161 | } else if (overlay) { 162 | return ; 163 | } else if (selected) { 164 | return ; 165 | } 166 | }; 167 | const cover = () => ( 168 | 169 | {collection.coverPhoto && ( 170 | 175 | )} 176 | 177 | 178 | {`${collection.totalPhotos ? collection.totalPhotos : 0} Photos`} 179 | 180 | {collection.title} 181 | {collection.isPrivate ? : null} 182 | {inRowSelection ? ( 183 | {selectionStatus()} 184 | ) : null} 185 | 186 | ); 187 | const content = () => { 188 | if (!inRowSelection) { 189 | return ( 190 | 191 | {cover()} 192 | 193 | ); 194 | } 195 | return cover(); 196 | }; 197 | return ( 198 | 203 | {content()} 204 | 205 | ); 206 | } 207 | } 208 | 209 | export default CollectionSView; 210 | -------------------------------------------------------------------------------- /src/components/Collections/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import EventListener from 'react-event-listener'; 5 | import styled from 'styled-components'; 6 | import CollectionView from '../Collection'; 7 | 8 | const ItemsWrapper = styled.div` 9 | width: 100%; 10 | display: flex; 11 | flex-direction: row; 12 | flex-wrap: wrap; 13 | box-sizing: border-box; 14 | margin: 0px auto; 15 | `; 16 | 17 | type Props = { 18 | loggedInUserId?: ?string, 19 | items: Object, 20 | onScrollToLoad: Function, 21 | }; 22 | 23 | const Collections = ({ loggedInUserId, items, onScrollToLoad }: Props) => { 24 | const handleResizeOrScroll = () => { 25 | const windowHeight = 26 | 'innerHeight' in window 27 | ? window.innerHeight 28 | : document && document.documentElement 29 | ? document.documentElement.offsetHeight 30 | : 0; 31 | const body = document.body; 32 | const html = document.documentElement; 33 | let docHeight = 0; 34 | if ( 35 | html && 36 | body && 37 | body.scrollHeight && 38 | body.offsetHeigh && 39 | html.offsetHeight && 40 | html.scrollHeight 41 | ) { 42 | docHeight = Math.max( 43 | body.scrollHeight, 44 | body.offsetHeight, 45 | html.clientHeight, 46 | html.scrollHeight, 47 | html.offsetHeight 48 | ); 49 | } 50 | const windowBottom = windowHeight + window.pageYOffset; 51 | if (windowBottom >= docHeight) { 52 | onScrollToLoad(); 53 | } 54 | }; 55 | const collections = () => ( 56 | 57 | {Object.keys(items).map((key, index) => ( 58 | 67 | ))} 68 | 69 | ); 70 | return ( 71 |
72 | 77 | {collections()} 78 |
79 | ); 80 | }; 81 | 82 | Collections.defaultProps = { 83 | loggedInUserId: null, 84 | }; 85 | 86 | export default Collections; 87 | -------------------------------------------------------------------------------- /src/components/CollectionsSView/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import styled from 'styled-components'; 5 | import { Link } from 'react-router-dom'; 6 | import CollectSmall from '../CollectionSView'; 7 | import { screenLargerThan } from '../../style/util'; 8 | import { dividerColor, secondaryColor1 } from '../../style/colors'; 9 | 10 | const ItemsWrapper = styled.div` 11 | display: none; 12 | padding: 20px 0px; 13 | border-bottom: 1px solid ${dividerColor}; 14 | ${screenLargerThan.phone` 15 | display: flex; 16 | flex-direction: column; 17 | `}; 18 | ${screenLargerThan.tablet` 19 | flex-direction: row; 20 | flex-wrap: wrap; 21 | justify-content: space-between; 22 | flex-basis: 25%; 23 | `}; 24 | `; 25 | 26 | const ViewAllWrapper = styled.div` 27 | flex-basis: 90px; 28 | display: flex; 29 | align-items: center; 30 | justify-content: center; 31 | ${screenLargerThan.tablet` 32 | flex-basis: 25%; 33 | `}; 34 | `; 35 | 36 | const ViewAll = styled(Link)` 37 | width: 100%; 38 | height: 74px; 39 | display: flex; 40 | justify-content: start; 41 | align-items: center; 42 | border: 1px solid ${dividerColor}; 43 | background-color: ${secondaryColor1}; 44 | border-radius: 10px; 45 | color: white; 46 | font-size: 24px; 47 | font-weight: 400; 48 | margin: 8px 0px; 49 | padding-left: 35px; 50 | ${screenLargerThan.tablet` 51 | height: 94px; 52 | justify-content: center; 53 | padding-left: 0px; 54 | margin: 0px 4px; 55 | font-size: 26px; 56 | font-weight: 600; 57 | `}; 58 | `; 59 | 60 | type Props = { 61 | items: Object, 62 | viewAllPath: string, 63 | }; 64 | 65 | const CollectionsSView = ({ items, viewAllPath }: Props) => ( 66 | 67 | {Object.keys(items) 68 | .slice(0, 3) 69 | .map(key => ( 70 | 71 | ))} 72 | 73 | 74 | View All 75 | 76 | 77 | 78 | ); 79 | 80 | export default CollectionsSView; 81 | -------------------------------------------------------------------------------- /src/components/Dialog/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | //$FlowFixMe we should import Node as type but the eslint doesn't happy 4 | import React, { Component, Node } from 'react'; 5 | import onClickOutside from 'react-onclickoutside'; 6 | import styled from 'styled-components'; 7 | import { rgba } from 'polished'; 8 | import { media } from '../../style/util'; 9 | import { white } from '../../style/colors'; 10 | 11 | // the dialog background 12 | const Overlay = styled.div` 13 | background-color: ${rgba(0, 0, 0, 0.6)}; 14 | position: fixed; 15 | overflow: auto; 16 | top: 0; 17 | left: 0; 18 | bottom: 0; 19 | right: 0; 20 | display: block; 21 | z-index: 900; 22 | width: 100%; 23 | padding: 0; 24 | margin: 0; 25 | border: 0; 26 | display: flex; 27 | justify-content: center; 28 | align-items: center; 29 | `; 30 | 31 | const Wrapper = styled.div` 32 | background-color: ${white}; 33 | border-radius: 3px; 34 | box-shadow: 0 2px 6px 0 ${rgba(0, 0, 0, 0.44)}; 35 | padding: 0; 36 | ${media.tablet` 37 | width: 100%; 38 | border-radius: 0px; 39 | box-shadow: none; 40 | `}; 41 | `; 42 | 43 | type InnerDialogProps = { 44 | children: Node, 45 | onRequestClose: Function, 46 | }; 47 | 48 | /** 49 | * Inner Component to handle onRequestClose on overlay area 50 | * NOTE: can't stateless component since we use from react-onclickoutside 51 | */ 52 | class Inner extends Component { 53 | handleClickOutside() { 54 | this.props.onRequestClose(); 55 | } 56 | 57 | render() { 58 | return {this.props.children}; 59 | } 60 | } 61 | 62 | /** 63 | * wrap this with onClickOutSide 64 | * to handle when click out side of dialog 65 | * used in Dialog component 66 | */ 67 | const InnerDialog = onClickOutside(Inner); 68 | 69 | type DialogProps = { 70 | children: Node, 71 | open: boolean, 72 | onRequestClose: Function, 73 | }; 74 | 75 | // the main component 76 | const Dialog = ({ children, open, onRequestClose, ...others }: DialogProps) => { 77 | const main = () => { 78 | if (open) { 79 | return ( 80 | 81 | {children} 82 | 83 | ); 84 | } 85 | return false; 86 | }; 87 | return main(); 88 | }; 89 | 90 | Dialog.defaultProps = { 91 | open: false, 92 | onRequestClose: () => {}, 93 | }; 94 | 95 | export default Dialog; 96 | -------------------------------------------------------------------------------- /src/components/ExtLink/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import styled from 'styled-components'; 4 | import { secondaryColor1, primaryColor1 } from '../../style/colors'; 5 | 6 | const ExtLink = styled.a` 7 | padding: 6px; 8 | color: ${secondaryColor1}; 9 | text-decoration: underline; 10 | transition: color 0.2s ease-in-out, opacity 0.2s ease-in-out; 11 | &:hover, 12 | &:focus { 13 | color: ${primaryColor1}; 14 | } 15 | `; 16 | 17 | export default ExtLink; 18 | -------------------------------------------------------------------------------- /src/components/Link/index.js: -------------------------------------------------------------------------------- 1 | //flow 2 | 3 | import styled from 'styled-components'; 4 | import { Link as _Li } from 'react-router-dom'; 5 | import { activeLinkColor, linkColor } from '../../style/colors'; 6 | 7 | const Link = styled(_Li)` 8 | color: ${linkColor}; 9 | &:hover { 10 | color: ${activeLinkColor}; 11 | } 12 | `; 13 | 14 | export default Link; 15 | -------------------------------------------------------------------------------- /src/components/NavOnAvatar/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import { connect } from 'react-redux'; 5 | import styled from 'styled-components'; 6 | import _Link from '../Link'; 7 | import _ExtLink from '../ExtLink'; 8 | import {} from '../../style/colors'; 9 | 10 | const Wrapper = styled.div` 11 | width: 100%; 12 | padding: 8px 0px; 13 | `; 14 | 15 | const List = styled.ul` 16 | margin-bottom: 0; 17 | `; 18 | 19 | const Item = styled.li` 20 | box-sizing: border-box; 21 | width: 100%; 22 | white-space: nowrap; 23 | font-size: 15px; 24 | font-weight: 300; 25 | `; 26 | 27 | const Link = styled(_Link)` 28 | padding: 7px 25px; 29 | display: block; 30 | `; 31 | 32 | const ExtLink = styled(_ExtLink)` 33 | padding: 7px 25px; 34 | display: block; 35 | `; 36 | 37 | type Props = { 38 | profileLink: string, 39 | }; 40 | 41 | const NavOnAvatar = ({ profileLink }: Props) => ( 42 | 43 | 44 | 45 | 46 | Profile 47 | 48 | 49 | 50 | 51 | Account Settings 52 | 53 | 54 | 55 | Logout 56 | 57 | 58 | 59 | ); 60 | 61 | const mapStateToProps = state => ({ profileLink: state.user.links.html }); 62 | 63 | export default connect(mapStateToProps)(NavOnAvatar); 64 | -------------------------------------------------------------------------------- /src/components/Navigation/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import { NavLink } from 'react-router-dom'; 5 | import styled from 'styled-components'; 6 | import { lighten } from 'polished'; 7 | import { 8 | dividerColor, 9 | linkColor, 10 | activeLinkColor, 11 | primaryColor1, 12 | } from '../../style/colors'; 13 | 14 | const Wrapper = styled.div` 15 | border-top: solid 1px ${dividerColor}; 16 | display: flex; 17 | justify-content: space-between; 18 | align-items: center; 19 | height: 36px; 20 | `; 21 | 22 | const List = styled.ul` 23 | display: inline-block; 24 | height: 100%; 25 | `; 26 | 27 | const Item = styled.li` 28 | display: inline-block; 29 | box-sizing: border-box; 30 | height: 100%; 31 | `; 32 | 33 | const Link = styled(NavLink)` 34 | padding: 4px 16px; 35 | display: inline-flex; 36 | align-items: center; 37 | height: 100%; 38 | color: ${lighten(0.1, linkColor)}; 39 | &:hover { 40 | color: ${activeLinkColor}; 41 | } 42 | `; 43 | 44 | type Props = {}; 45 | 46 | const Navigation = ({ ...others }: Props) => ( 47 | 48 | 49 | 50 | 56 | Home 57 | 58 | 64 | Your Collection 65 | 66 | 72 | Liked photos 73 | 74 | 75 | 76 | 77 | ); 78 | 79 | export default Navigation; 80 | -------------------------------------------------------------------------------- /src/components/Photos/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import EventListener from 'react-event-listener'; 5 | import Masonry, { ResponsiveMasonry } from 'react-responsive-masonry'; 6 | import PhotoComponent from '../Photo'; 7 | import { sizes } from '../../style/util'; 8 | 9 | type Props = { 10 | items: Object, 11 | onScrollToLoad: Function, 12 | }; 13 | 14 | const Photos = ({ items, onScrollToLoad, ...others }: Props) => { 15 | const handleResizeOrScroll = () => { 16 | const windowHeight = 17 | 'innerHeight' in window 18 | ? window.innerHeight 19 | : document && document.documentElement 20 | ? document.documentElement.offsetHeight 21 | : 0; 22 | const body = document.body; 23 | const html = document.documentElement; 24 | let docHeight = 0; 25 | if ( 26 | html && 27 | body && 28 | body.scrollHeight && 29 | body.offsetHeigh && 30 | html.offsetHeight && 31 | html.scrollHeight 32 | ) { 33 | docHeight = Math.max( 34 | body.scrollHeight, 35 | body.offsetHeight, 36 | html.clientHeight, 37 | html.scrollHeight, 38 | html.offsetHeight 39 | ); 40 | } 41 | const windowBottom = windowHeight + window.pageYOffset; 42 | if (windowBottom >= docHeight) { 43 | onScrollToLoad(); 44 | } 45 | }; 46 | return ( 47 |
48 | 53 | 59 | 60 | {Object.keys(items).map(key => ( 61 | 62 | ))} 63 | 64 | 65 |
66 | ); 67 | }; 68 | 69 | export default Photos; 70 | -------------------------------------------------------------------------------- /src/components/PrivateRoute/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | //$FlowFixMe we should import Node as type but the eslint doesn't happy 4 | import React, { ReactPropTypes } from 'react'; 5 | import { Route, Redirect } from 'react-router-dom'; 6 | import { getState } from '../../store'; 7 | 8 | /** 9 | * NOTE: you can't wrap component with connect from react-redux, since the Route functionality doesn't work 10 | */ 11 | type Props = { 12 | component: ReactPropTypes, 13 | path: string, 14 | }; 15 | 16 | const PrivateRoute = ({ component: Component, path }: Props) => ( 17 | 20 | getState().user.isAuthorized ? ( 21 | 22 | ) : ( 23 | 28 | ) 29 | } 30 | /> 31 | ); 32 | 33 | export default PrivateRoute; 34 | -------------------------------------------------------------------------------- /src/components/Progress/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import { connect } from 'react-redux'; 5 | import styled, { keyframes } from 'styled-components'; 6 | import { primaryColor1 } from '../../style/colors'; 7 | import { media } from '../../style/util'; 8 | 9 | const lpMover = keyframes` 10 | from { 11 | right: 100%; 12 | } 13 | to { 14 | right: 0%; 15 | } 16 | `; 17 | 18 | const Mover = styled.div` 19 | position: fixed; 20 | top: 0; 21 | right: 0; 22 | overflow: auto; 23 | background-color: ${primaryColor1}; 24 | height: 4px; 25 | animation: ${lpMover} 2s cubic-bezier(0.07, 0.35, 0.98,-0.12) 0s infinite normal none running; 26 | z-index: 101; 27 | opacity: 1; 28 | ${media.giant` 29 | width: 390px; 30 | `} 31 | ${media.desktop` 32 | width: 330px; 33 | `} 34 | ${media.tablet` 35 | width: 256px; 36 | `} 37 | ${media.phone` 38 | width: 125px; 39 | `} 40 | `; 41 | 42 | type Props = { 43 | jobNumbers: number, 44 | }; 45 | 46 | const Progress = ({ jobNumbers }: Props) => { 47 | const progress = () => { 48 | if (jobNumbers > 0) { 49 | return ; 50 | } 51 | }; 52 | return
{progress()}
; 53 | }; 54 | 55 | const mapStateToProps = state => ({ jobNumbers: state.app.jobRunning }); 56 | 57 | export default connect(mapStateToProps)(Progress); 58 | -------------------------------------------------------------------------------- /src/components/RTextInput/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import TextInput from '../TextInput'; 5 | 6 | /** 7 | * render text input 8 | * this is for redux-form component for Field 9 | * INFO: http://redux-form.com/6.8.0/docs/api/Field.md/ 10 | */ 11 | 12 | type Props = { 13 | label: string, 14 | translate: Function, 15 | meta: { 16 | touched: boolean, 17 | error: boolean, 18 | }, 19 | input: Object, 20 | }; 21 | 22 | const RTextInput = ({ 23 | label, 24 | translate, 25 | meta: { touched, error }, 26 | input, 27 | ...others 28 | }: Props) => ( 29 | 35 | ); 36 | 37 | export default RTextInput; 38 | -------------------------------------------------------------------------------- /src/components/SvgIcon/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | //$FlowFixMe we should import Node as type but the eslint doesn't happy 4 | import React, { Component, Node } from 'react'; 5 | import styled from 'styled-components'; 6 | import { iconColor } from '../../style/colors'; 7 | 8 | const Svg = styled.svg` 9 | display: 'inline-block'; 10 | color: ${props => props.color}; 11 | height: ${props => `${props.size}px`}; 12 | width: ${props => `${props.size}px`}; 13 | user-select: 'none'; 14 | ${props => 15 | props.fillFromParent 16 | ? `` 17 | : `fill: ${props.hovered ? props.hoverColor : props.color};`}; 18 | `; 19 | 20 | type Props = { 21 | children: Node, 22 | size?: number, 23 | fillFromParent?: boolean, 24 | color?: string, 25 | hoverColor?: string, 26 | viewBox?: string, 27 | onMouseEnter?: Function, 28 | onMouseLeave?: Function, 29 | }; 30 | 31 | type State = { 32 | hovered: boolean, 33 | }; 34 | 35 | class SvgIcon extends Component { 36 | static defaultProps = { 37 | color: iconColor, 38 | size: 24, 39 | fillFromParent: false, 40 | /** 41 | * Allows you to redefine what the coordinates 42 | * without units mean inside an svg element. For example, 43 | * if the SVG element is 500 (width) by 200 (height), and you 44 | * pass viewBox="0 0 50 20", this means that the coordinates inside 45 | * the svg will go from the top left corner (0,0) to bottom right (50,20) 46 | * and each unit will be worth 10px. 47 | */ 48 | viewBox: '0 0 24 24', 49 | }; 50 | state = { 51 | hovered: false, 52 | }; 53 | 54 | handleMouseLeave = (e: Event) => { 55 | this.setState({ hovered: false }); 56 | if (this.props.onMouseLeave) { 57 | this.props.onMouseLeave(e); 58 | } 59 | }; 60 | 61 | handleMouseEnter = (e: Event) => { 62 | this.setState({ hovered: true }); 63 | if (this.props.onMouseEnter) { 64 | this.props.onMouseEnter(e); 65 | } 66 | }; 67 | render() { 68 | const { 69 | size, 70 | color, 71 | hoverColor, 72 | viewBox, 73 | fillFromParent, 74 | children, 75 | ...others 76 | } = this.props; 77 | const { hovered } = this.state; 78 | return ( 79 | 89 | {children} 90 | 91 | ); 92 | } 93 | } 94 | 95 | export default SvgIcon; 96 | -------------------------------------------------------------------------------- /src/components/SvgImage/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | //$FlowFixMe we should import Node as type but the eslint doesn't happy 4 | import React, { Node } from 'react'; 5 | import styled from 'styled-components'; 6 | import { primaryColor1 } from '../../style/colors'; 7 | 8 | const SvgImg = styled.svg` 9 | display: inline-block; 10 | color: ${props => props.color}; 11 | height: ${props => `${props.size}px`}; 12 | width: ${props => `${props.size}px`}; 13 | user-select: 'none'; 14 | ${props => 15 | props.fillFromParent 16 | ? `` 17 | : `fill: ${props.hovered ? props.hoverColor : props.color};`}; 18 | `; 19 | 20 | type Props = { 21 | children: Node, 22 | viewBox: string, 23 | size?: number, 24 | color?: string, 25 | fillFromParent?: boolean, 26 | }; 27 | 28 | const SvgImage = ({ 29 | children, 30 | viewBox, 31 | size, 32 | color, 33 | fillFromParent, 34 | ...others 35 | }: Props) => ( 36 | 42 | {children} 43 | 44 | ); 45 | 46 | SvgImage.defaultProps = { 47 | size: 20, 48 | color: primaryColor1, 49 | fillFromParent: false, 50 | }; 51 | 52 | export default SvgImage; 53 | -------------------------------------------------------------------------------- /src/components/TextInput/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import styled from 'styled-components'; 5 | import { lighten, darken } from 'polished'; 6 | import { 7 | inputBgColor, 8 | textColor2, 9 | errorColor, 10 | warnColor, 11 | successColor, 12 | } from '../../style/colors'; 13 | 14 | const Input = styled.input` 15 | background-color: ${inputBgColor}; 16 | background-color: ${props => 17 | props.msgColor ? lighten(0.27, props.msgColor) : inputBgColor}; 18 | border: ${props => 19 | props.msgColor 20 | ? `1px solid${darken(0.1, props.msgColor)}` 21 | : '1px solid transparent'}; 22 | box-shadow: none; 23 | position: relative; 24 | padding: 12px; 25 | height: 36px; 26 | width: ${props => (props.fullWidth ? '100%' : '200px')}; 27 | border-radius: ${props => (props.rounded ? '20px' : '3px')}; 28 | box-sizing: border-box; 29 | font-family: inherit; 30 | font-size: 14px; 31 | font-weight: normal; 32 | outline: none; 33 | line-height: 16px; 34 | color: ${textColor2}; 35 | ${props => 36 | props.disabled 37 | ? ` 38 | opacity: 0.6; 39 | ` 40 | : ``} &:focus { 41 | border-color: ${darken(0.1, inputBgColor)}; 42 | background-color: ${props => 43 | props.msgColor 44 | ? lighten(0.27, props.msgColor) 45 | : lighten(0.07, inputBgColor)}; 46 | -webkit-box-shadow: 0 0 0 1000px 47 | ${props => 48 | props.msgColor 49 | ? lighten(0.27, props.msgColor) 50 | : lighten(0.07, inputBgColor)} 51 | inset !important; 52 | } 53 | &:-webkit-autofill { 54 | -webkit-box-shadow: 0 0 0 1000px 55 | ${props => 56 | props.msgColor ? lighten(0.27, props.msgColor) : inputBgColor} 57 | inset; 58 | } 59 | `; 60 | 61 | const AreaInput = styled.textarea` 62 | resize: none; 63 | background-color: ${props => 64 | props.msgColor ? lighten(0.27, props.msgColor) : inputBgColor}; 65 | width: ${props => (props.fullWidth ? '100%' : 'none')}; 66 | border: ${props => 67 | props.msgColor ? `1px solid${darken(0.1, props.msgColor)}` : '0px'}; 68 | box-shadow: none; 69 | position: relative; 70 | border-radius: 3px; 71 | padding: ${props => (props.multiLanguage ? '10px 10px 24px 10px' : '10px')}; 72 | box-sizing: border-box; 73 | font-family: IRANSans, Helvetica, Verdana, sans-serif; 74 | font-size: 14px; 75 | font-weight: normal; 76 | outline: none; 77 | color: ${textColor2}; 78 | ${props => 79 | props.disabled 80 | ? ` 81 | opacity: 0.6; 82 | ` 83 | : ``} &:focus { 84 | outline: ${`${darken(0.35, inputBgColor)} auto 1px !important`}; 85 | background-color: ${props => 86 | props.msgColor 87 | ? `${props.msgColor} !important` 88 | : `${lighten(0.07, inputBgColor)} !important`}; 89 | } 90 | `; 91 | 92 | const Messgae = styled.p` 93 | color: ${props => props.color}; 94 | font-size: 13px; 95 | padding: 6px 3px; 96 | `; 97 | 98 | const getColorOfMessage = msgType => { 99 | switch (msgType) { 100 | case 'error': 101 | return errorColor; 102 | case 'warn': 103 | return warnColor; 104 | default: 105 | // 'success' 106 | return successColor; 107 | } 108 | }; 109 | 110 | type Props = { 111 | wrapperStyle?: Object, 112 | disabled?: boolean, 113 | fullWidth?: boolean, 114 | rounded?: boolean, 115 | hintText?: string, 116 | multiLine?: boolean, 117 | message?: ?string, 118 | messageType?: 'error' | 'warn' | 'success', 119 | messageColor?: string, 120 | }; 121 | 122 | const TextInput = ({ 123 | wrapperStyle, 124 | disabled, 125 | fullWidth, 126 | hintText, 127 | multiLine, 128 | message, 129 | messageType, 130 | messageColor, 131 | rounded, 132 | ...others 133 | }: Props) => { 134 | const msgColor = messageColor ? messageColor : getColorOfMessage(messageType); 135 | const input = () => { 136 | if (multiLine) { 137 | return ( 138 | 146 | ); 147 | } 148 | return ( 149 | 159 | ); 160 | }; 161 | const msg = () => { 162 | if (message) { 163 | return {message}; 164 | } 165 | }; 166 | return ( 167 |
168 | {input()} 169 | {msg()} 170 |
171 | ); 172 | }; 173 | 174 | TextInput.defaultProps = { 175 | wrapperStyle: {}, 176 | fullWidth: false, 177 | rounded: false, 178 | disabled: false, 179 | hintText: '', 180 | multiLine: false, 181 | message: null, 182 | messageType: 'error', 183 | messageColor: errorColor, 184 | }; 185 | 186 | export default TextInput; 187 | -------------------------------------------------------------------------------- /src/components/svg-icons/add.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import SvgIcon from '../SvgIcon'; 5 | 6 | type Props = {}; 7 | 8 | const AddIcon = ({ ...others }: Props) => ( 9 | 10 | 11 | 12 | ); 13 | 14 | export default AddIcon; 15 | -------------------------------------------------------------------------------- /src/components/svg-icons/close.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import SvgIcon from '../SvgIcon'; 5 | 6 | type Props = {}; 7 | 8 | const CloseIcon = ({ ...others }: Props) => ( 9 | 10 | 11 | 12 | ); 13 | 14 | export default CloseIcon; 15 | -------------------------------------------------------------------------------- /src/components/svg-icons/done.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import SvgIcon from '../SvgIcon'; 5 | 6 | type Props = {}; 7 | 8 | const DoneIcon = ({ ...others }: Props) => ( 9 | 10 | 11 | 12 | ); 13 | 14 | export default DoneIcon; 15 | -------------------------------------------------------------------------------- /src/components/svg-icons/download.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import SvgIcon from '../SvgIcon'; 5 | 6 | type Props = {}; 7 | 8 | const DownloadIcon = ({ ...others }: Props) => ( 9 | 10 | 11 | 12 | ); 13 | 14 | export default DownloadIcon; 15 | -------------------------------------------------------------------------------- /src/components/svg-icons/edit.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import SvgIcon from '../SvgIcon'; 5 | 6 | type Props = {}; 7 | 8 | const EditIcon = ({ ...others }: Props) => ( 9 | 10 | 11 | 12 | ); 13 | 14 | export default EditIcon; 15 | -------------------------------------------------------------------------------- /src/components/svg-icons/like.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import SvgIcon from '../SvgIcon'; 5 | 6 | type Props = {}; 7 | 8 | const LikeIcon = ({ ...others }: Props) => ( 9 | 10 | 11 | 12 | ); 13 | 14 | export default LikeIcon; 15 | -------------------------------------------------------------------------------- /src/components/svg-icons/lock.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import SvgIcon from '../SvgIcon'; 5 | 6 | type Props = {}; 7 | 8 | const LockIcon = ({ ...others }: Props) => ( 9 | 10 | 11 | 12 | ); 13 | 14 | export default LockIcon; 15 | -------------------------------------------------------------------------------- /src/components/svg-icons/remove.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import SvgIcon from '../SvgIcon'; 5 | 6 | type Props = {}; 7 | 8 | const RemoveIcon = ({ ...others }: Props) => ( 9 | 10 | 11 | 12 | ); 13 | 14 | export default RemoveIcon; 15 | -------------------------------------------------------------------------------- /src/components/svg-images/camera.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | import React from 'react'; 3 | import SvgImage from '../SvgImage'; 4 | 5 | type Props = {}; 6 | 7 | const Camera = ({ ...others }: Props) => ( 8 | 9 | 10 | 11 | ); 12 | 13 | export default Camera; 14 | -------------------------------------------------------------------------------- /src/constants/action-types.js: -------------------------------------------------------------------------------- 1 | // app action 2 | export const SE_LAST_PATH_NAME = 'app/SE_LAST_PATH_NAME'; 3 | export const CL_STORE = 'app/CL_STORE'; 4 | export const CH_JOB_ST = 'app/CH_JOB_ST'; 5 | export const NOTHING = 'app/NOTHING'; 6 | export const SE_ACTION_DATA = 'app/SE_ACTION_DATA'; 7 | 8 | // user action 9 | export const GE_ACCESS_TOKEN = 'user/GE_ACCESS_TOKEN'; 10 | export const SE_ACCESS_TOKEN = 'user/SE_ACCESS_TOKEN'; 11 | export const LOGOUT = 'user/LOGOUT'; 12 | export const GE_USER_PROFILE = 'user/GE_USER_PROFILE'; 13 | export const SE_USER_PROFILE = 'user/SE_USER_PROFILE'; 14 | 15 | // photo action 16 | export const GE_PHOTOS = 'photo/GE_PHOTOS'; 17 | export const LIKE_PHOTO = 'photo/LIKE_PHOTO'; 18 | export const UNLIKE_PHOTO = 'photo/UNLIKE_PHOTO'; 19 | export const SEARCH_PHOTOS = 'photo/SEARCH_PHOTOS'; 20 | export const GE_PHOTO = 'photo/GE_PHOTO'; 21 | 22 | // items actions 23 | export const SE_ITEM = 'item/SE_ITEM'; 24 | export const SE_ITEMS = 'item/SE_ITEMS'; 25 | export const CL_ITEMS = 'item/CL_ITEMS'; 26 | export const UP_ITEM = 'item/UP_ITEM'; 27 | export const UP_FIELD_OF_ITEM = 'item/UP_FIELD_OF_ITEM'; 28 | export const RM_ITEM = 'item/RM_ITEM'; 29 | export const SE_ITEMS_ATTR = 'item/SE_ITEMS_ATTR'; 30 | 31 | // collection actions 32 | export const GE_USER_COLLECTIONS = 'collection/GE_USER_COLLECTIONS'; 33 | export const GE_COLLECTION = 'collection/GE_COLLECTION'; 34 | export const GE_COLLECTION_PHOTOS = 'collection/GE_COLLECTION_PHOTOS'; 35 | export const SEARCH_COLLECTIONS = 'collection/SEARCH_COLLECTIONS'; 36 | export const DELETE_COLLECTION = 'collection/DELETE_COLLECTION'; 37 | export const CREATE_COLLECTION = 'collection/CREATE_COLLECTION'; 38 | export const UPDATE_COLLECTION = 'collection/UPDATE_COLLECTION'; 39 | export const CONFIRM_DELETE_COLLECTION = 'collection/CONFIRM_DELETE_COLLECTION'; 40 | export const ADD_PHOTO_TO_COLLECTION = 'collection/ADD_PHOTO_TO_COLLECTION'; 41 | export const REMOVE_PHOTO_FROM_COLLECTION = 42 | 'collection/REMOVE_PHOTO_FROM_COLLECTION'; 43 | -------------------------------------------------------------------------------- /src/constants/api-error-codes.js: -------------------------------------------------------------------------------- 1 | export const UN_AVAILABLE = 14; 2 | export const NOT_FOUND = 404; 3 | export const UNHANDLED = 900; 4 | -------------------------------------------------------------------------------- /src/constants/service-info.js: -------------------------------------------------------------------------------- 1 | // the environment variables form `.env` file and set it via env-cmd 2 | export const ROOT_URL = process.env.REACT_APP_ROOT_URL; 3 | export const API_ROOT = process.env.REACT_APP_API_ROOT; 4 | export const CLIENT_ID = process.env.REACT_APP_CLIENT_ID; 5 | export const CLIENT_SECRET = process.env.REACT_APP_CLIENT_SECRET; 6 | export const OAUTH_PATH = process.env.REACT_APP_OAUTH_PATH; 7 | export const REDIRECT_URI = process.env.REACT_APP_REDIRECT_URI; 8 | -------------------------------------------------------------------------------- /src/containers/AddOrEditCollectionDialog/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import { connect } from 'react-redux'; 5 | import { push } from 'react-router-redux'; 6 | import Dialog from '../../components/Dialog'; 7 | import AddOrEditCollection from '../AddOrEditCollection'; 8 | 9 | type Props = { 10 | pathname: string, 11 | lastPathname: string, 12 | onPush: Function, 13 | }; 14 | 15 | const AddOrEditCollectionDialog = ({ 16 | pathname, 17 | lastPathname, 18 | onPush, 19 | }: Props) => ( 20 | onPush(lastPathname)}> 26 | onPush(lastPathname)} /> 27 | 28 | ); 29 | 30 | const mapStateToProps = state => ({ 31 | pathname: state.router.location.pathname, 32 | lastPathname: state.app.lastPathname, 33 | }); 34 | 35 | export default connect( 36 | mapStateToProps, 37 | { 38 | onPush: push, 39 | } 40 | )(AddOrEditCollectionDialog); 41 | -------------------------------------------------------------------------------- /src/containers/AddToCollectionDialog/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import { connect } from 'react-redux'; 5 | import { push } from 'react-router-redux'; 6 | import Dialog from '../../components/Dialog'; 7 | import AddToCollection from '../AddToCollection'; 8 | 9 | type Props = { 10 | searchPath: string, 11 | lastPathname: string, 12 | onPush: Function, 13 | }; 14 | 15 | const AddToCollectionDialog = ({ searchPath, lastPathname, onPush }: Props) => ( 16 | onPush(lastPathname)}> 19 | onPush(lastPathname)} /> 20 | 21 | ); 22 | 23 | const mapStateToProps = state => ({ 24 | searchPath: state.router.location.search, 25 | lastPathname: state.app.lastPathname, 26 | }); 27 | 28 | export default connect( 29 | mapStateToProps, 30 | { 31 | onPush: push, 32 | } 33 | )(AddToCollectionDialog); 34 | -------------------------------------------------------------------------------- /src/containers/App/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React from 'react'; 4 | import styled from 'styled-components'; 5 | import { Route, Switch, Redirect } from 'react-router-dom'; 6 | import Header from '../Header'; 7 | import Home from '../Home'; 8 | import Search from '../Search'; 9 | import UserCollections from '../UserCollections'; 10 | import LikedPhotos from '../LikedPhotos'; 11 | import NotFound from '../NotFound'; 12 | import PhotosByCollection from '../PhotosByCollection'; 13 | import { maxWidthContent } from '../../style/util'; 14 | 15 | const Wrapper = styled.div` 16 | height: 100%; 17 | `; 18 | 19 | const Main = styled.div` 20 | max-width: ${`${maxWidthContent}px`}; 21 | margin: 0 auto; 22 | width: 100%; 23 | height: 100%; 24 | top: 0; 25 | `; 26 | 27 | const Content = styled.div` 28 | margin-top: 15px; 29 | padding: 0px 16px; 30 | `; 31 | 32 | const App = () => ( 33 | 34 |
35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
51 | 52 | ); 53 | 54 | export default App; 55 | -------------------------------------------------------------------------------- /src/containers/Authorize/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import { Component } from 'react'; 4 | import { getAccessToken } from '../../actions/user'; 5 | import { getStore, getHistory } from '../../store'; 6 | 7 | type Props = {}; 8 | 9 | class Authorize extends Component { 10 | componentDidMount() { 11 | // handle get access token with authorization code 12 | // NOTE: more information at 13 | // https://stackoverflow.com/questions/43216569/how-to-get-query-parameters-in-r 14 | // e act-router-v4 should get query string from history 15 | const { dispatch } = getStore(); 16 | const URLQStrings = new URLSearchParams(getHistory().location.search); 17 | const code: string = URLQStrings.has('code') ? URLQStrings.get('code') : ''; 18 | // get access token 19 | dispatch(getAccessToken(code)); 20 | } 21 | 22 | render() { 23 | return false; 24 | } 25 | } 26 | 27 | export default Authorize; 28 | -------------------------------------------------------------------------------- /src/containers/Header/index.js: -------------------------------------------------------------------------------- 1 | //@flow 2 | 3 | import React, { Component } from 'react'; 4 | import { connect } from 'react-redux'; 5 | import { withRouter } from 'react-router-dom'; 6 | import EventListener from 'react-event-listener'; 7 | import { push } from 'react-router-redux'; 8 | import debounce from 'lodash/debounce'; 9 | import styled from 'styled-components'; 10 | import _Logo from '../../components/svg-images/camera'; 11 | import _Nav from '../../components/Navigation'; 12 | import TextInput from '../../components/TextInput'; 13 | import Popover from '../../components/Popover'; 14 | import Avatar from '../../components/Avatar'; 15 | import NavOnAvatar from '../../components/NavOnAvatar'; 16 | import { maxWidthContent } from '../../style/util'; 17 | import { white, dividerColor } from '../../style/colors'; 18 | import type { UserProfile as UserProfileType } from '../../types/data'; 19 | 20 | const Wrapper = styled.div` 21 | background-color: ${white}; 22 | `; 23 | 24 | const Logo = styled(_Logo)` 25 | width: 28px; 26 | height: 28px; 27 | `; 28 | 29 | const TopBarWrapper = styled.div` 30 | background-color: ${white}; 31 | height: 65px; 32 | width: 100%; 33 | ${props => 34 | props.fixed 35 | ? ` 36 | position: fixed; 37 | z-index: 9999; 38 | border-bottom: solid 1px ${dividerColor}; 39 | ` 40 | : ` 41 | position: relative; 42 | `}; 43 | `; 44 | 45 | const TopBar = styled.div` 46 | max-width: ${`${maxWidthContent}px`}; 47 | width: 100%; 48 | height: 100%; 49 | top: 0; 50 | margin: 0 auto; 51 | display: flex; 52 | justify-content: space-between; 53 | align-items: center; 54 | padding: 0px 8px; 55 | `; 56 | 57 | const SearchTx = styled(TextInput)` 58 | flex: 1; 59 | margin: 0px 16px; 60 | `; 61 | 62 | const Controller = styled.div` 63 | display: flex; 64 | align-items: center; 65 | `; 66 | 67 | const AButton = styled.button` 68 | cursor: pointer; 69 | `; 70 | 71 | const Nav = styled(_Nav)` 72 | max-width: ${`${maxWidthContent}px`}; 73 | width: 100%; 74 | margin: 0 auto; 75 | `; 76 | 77 | type Props = { 78 | queryValue: string, 79 | userProfile: UserProfileType, 80 | onPush: Function, 81 | }; 82 | 83 | type State = { 84 | lastScrollTop: number, 85 | topBarFixed: boolean, 86 | searchTxVal: string, 87 | }; 88 | 89 | class Header extends Component { 90 | static defaultProps = { 91 | delayedCallback: () => {}, 92 | }; 93 | state = { 94 | lastScrollTop: 0, 95 | topBarFixed: false, 96 | searchTxVal: '', 97 | }; 98 | 99 | componentWillMount() { 100 | // $FlowFixMe 101 | this.delayedCallback = debounce(e => { 102 | // `event.target` is accessible now 103 | const val = e.target.value.replace(' ', '-'); 104 | if (val.trim().length > 0) { 105 | // push it to search 106 | this.props.onPush(`/search/${val}`); 107 | } else { 108 | // return to home 109 | this.props.onPush('/'); 110 | } 111 | }, 700); 112 | this.setState({ searchTxVal: this.props.queryValue }); 113 | } 114 | 115 | componentWillReceiveProps(nextProps) { 116 | if ( 117 | nextProps.queryValue !== this.props.queryValue && 118 | nextProps.queryValue !== this.state.searchTxVal && 119 | nextProps.queryValue === '' 120 | ) { 121 | this.setState({ searchTxVal: '' }); 122 | } 123 | } 124 | 125 | shouldComponentUpdate(nextProps, nextState) { 126 | // prevent to re render view in update lastScrollTop state 127 | if (nextState.lastScrollTop !== this.state.lastScrollTop) { 128 | return false; 129 | } 130 | return true; 131 | } 132 | 133 | onSearchTxChange = e => { 134 | e.persist(); 135 | this.setState({ searchTxVal: e.target.value }); 136 | // $FlowFixMe 137 | this.delayedCallback(e); 138 | }; 139 | 140 | handleScroll = e => { 141 | const { lastScrollTop } = this.state; 142 | const scrollTop = e.target.body.scrollTop; 143 | if (scrollTop < lastScrollTop && scrollTop > 0) { 144 | this.setState({ topBarFixed: true }); 145 | } else { 146 | this.setState({ topBarFixed: false }); 147 | } 148 | // set this scrollTop as last 149 | this.setState({ lastScrollTop: scrollTop }); 150 | }; 151 | 152 | render() { 153 | const { userProfile } = this.props; 154 | const { topBarFixed, searchTxVal } = this.state; 155 | const avatar = () => { 156 | if (userProfile.profileImage) { 157 | return ( 158 | 165 | 169 | 170 | }> 171 | 172 | 173 | ); 174 | } 175 | return null; 176 | }; 177 | return ( 178 | 179 | 180 | 181 | 182 | 183 | 184 | {' '} 190 | {avatar()} 191 | 192 | 193 | 194 |