├── .all-contributorsrc ├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .storybook └── config.js ├── .travis.yml ├── .vscode └── settings.json ├── License.md ├── Readme.md ├── add-duration ├── .gitignore ├── index.js ├── package.json ├── yarn.lock └── youtube.js ├── bot ├── .gitignore ├── index.js ├── package.json └── yarn.lock ├── gh.png ├── package.json ├── public └── favicon.ico ├── razzle.config.js ├── src ├── App.js ├── Components │ ├── AddTalk.js │ ├── CinemaMode │ │ ├── index.js │ │ └── story.js │ ├── Container.js │ ├── CookieBanner.js │ ├── Errors │ │ └── Error404.js │ ├── Favorite │ │ ├── example.js │ │ ├── index.js │ │ ├── story.js │ │ └── view.js │ ├── Filters │ │ ├── Duration.js │ │ ├── Order.js │ │ └── Year.js │ ├── Header │ │ └── index.js │ ├── HideViewed │ │ ├── example.js │ │ ├── index.js │ │ ├── story.js │ │ └── view.js │ ├── MetaTags │ │ ├── FourOfFour.js │ │ ├── Home.js │ │ ├── Speaker.js │ │ ├── Speakers.js │ │ ├── Tag.js │ │ ├── Tags.js │ │ └── Video.js │ ├── Nav.js │ ├── Player │ │ ├── example.js │ │ ├── index.js │ │ └── story.js │ ├── Query.js │ ├── Scroll.js │ ├── Search.js │ ├── Styling │ │ ├── Button.js │ │ ├── Forms.js │ │ ├── Heart.js │ │ ├── Input.js │ │ ├── Item.js │ │ ├── Loading.js │ │ ├── Play.js │ │ ├── Section.js │ │ ├── Speaker.js │ │ ├── Tag.js │ │ ├── Text.js │ │ └── __tests__ │ │ │ ├── Button.js │ │ │ ├── Heart.js │ │ │ ├── Input.js │ │ │ ├── Item.js │ │ │ ├── Loading.js │ │ │ ├── Play.js │ │ │ ├── Speaker.js │ │ │ └── __snapshots__ │ │ │ ├── Button.js.snap │ │ │ ├── Heart.js.snap │ │ │ ├── Input.js.snap │ │ │ ├── Item.js.snap │ │ │ ├── Loading.js.snap │ │ │ ├── Play.js.snap │ │ │ └── Speaker.js.snap │ ├── Tag.js │ ├── Talks.js │ ├── TalksList.js │ ├── ToggleMode.js │ ├── Video │ │ ├── example.js │ │ ├── index.js │ │ └── story.js │ ├── VideoInfo.js │ └── Watched.js ├── Pages │ ├── Favorites.js │ ├── FourOFour.js │ ├── Home.js │ ├── Proposed.js │ ├── Speaker.js │ ├── Speakers.js │ ├── Tag.js │ ├── Tags.js │ └── Video.js ├── Queries │ ├── ADD_VIDEO.js │ ├── ALL_VIDEOS.js │ ├── COUNT.js │ ├── Fragments │ │ └── Video.js │ ├── GET_PROPOSED_TALKS.js │ ├── GET_VIDEOS.js │ ├── Local │ │ ├── ADD_FAVORITE.js │ │ ├── ADD_WATCHED.js │ │ ├── GET_FAVORITES.js │ │ ├── GET_SEARCH.js │ │ ├── GET_WATCHED.js │ │ ├── GET__SPEAKERS_SEARCH.js │ │ ├── GET__TAGS_SEARCH.js │ │ ├── REMOVE_FAVORITE.js │ │ ├── REMOVE_WATCHED.js │ │ ├── SHOW_VIEWED.js │ │ └── SWITCH_MODE.js │ ├── SPEAKERS.js │ ├── SPEAKER_VIDEOS.js │ ├── TAGS.js │ ├── TAG_VIDEOS.js │ └── VIDEO_DATA.js ├── Tests │ ├── setup.js │ └── shallow.js ├── Utils │ ├── global-styles.js │ ├── icons.js │ ├── link-parser.js │ ├── rss.js │ ├── search.js │ ├── state.js │ ├── stateLink.js │ ├── strings.js │ ├── theme.js │ └── youtube.js ├── assets │ ├── clear.svg │ ├── done.svg │ ├── icons │ │ ├── android-chrome-512x512.png │ │ ├── android-icon-144x144.png │ │ ├── android-icon-192x192.png │ │ ├── android-icon-36x36.png │ │ ├── android-icon-48x48.png │ │ ├── android-icon-72x72.png │ │ ├── android-icon-96x96.png │ │ ├── apple-icon-114x114.png │ │ ├── apple-icon-120x120.png │ │ ├── apple-icon-144x144.png │ │ ├── apple-icon-152x152.png │ │ ├── apple-icon-180x180.png │ │ ├── apple-icon-57x57.png │ │ ├── apple-icon-60x60.png │ │ ├── apple-icon-72x72.png │ │ ├── apple-icon-76x76.png │ │ ├── apple-icon-precomposed.png │ │ ├── apple-icon.png │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── ms-icon-144x144.png │ │ ├── ms-icon-150x150.png │ │ ├── ms-icon-310x310.png │ │ ├── ms-icon-70x70.png │ │ └── mstile-150x150.png │ ├── loading.svg │ ├── logo.svg │ ├── seen.svg │ └── twitter.svg ├── client.js ├── index.js ├── manifest.json └── server.js └── yarn.lock /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "awesome-talks", 3 | "projectOwner": "SaraVieira", 4 | "repoType": "github", 5 | "repoHost": "https://github.com", 6 | "files": [ 7 | "Readme.md" 8 | ], 9 | "imageSize": 100, 10 | "commit": true, 11 | "contributors": [ 12 | { 13 | "login": "hosmelq", 14 | "name": "Hosmel Quintana", 15 | "avatar_url": "https://avatars2.githubusercontent.com/u/1166143?v=4", 16 | "profile": "http://hosmelq.com", 17 | "contributions": [ 18 | "bug" 19 | ] 20 | }, 21 | { 22 | "login": "cain", 23 | "name": "Cain Hall", 24 | "avatar_url": "https://avatars1.githubusercontent.com/u/15246256?v=4", 25 | "profile": "http://cainhall.com.au", 26 | "contributions": [ 27 | "code" 28 | ] 29 | }, 30 | { 31 | "login": "glennreyes", 32 | "name": "Glenn Reyes", 33 | "avatar_url": "https://avatars1.githubusercontent.com/u/5080854?v=4", 34 | "profile": "https://twitter.com/glnnrys", 35 | "contributions": [ 36 | "bug" 37 | ] 38 | }, 39 | { 40 | "login": "pksjce", 41 | "name": "Pavithra Kodmad", 42 | "avatar_url": "https://avatars2.githubusercontent.com/u/417268?v=4", 43 | "profile": "http://pavithrakodmad.com", 44 | "contributions": [ 45 | "bug" 46 | ] 47 | }, 48 | { 49 | "login": "varjmes", 50 | "name": "James", 51 | "avatar_url": "https://avatars0.githubusercontent.com/u/542140?v=4", 52 | "profile": "https://jmes.tech", 53 | "contributions": [ 54 | "platform" 55 | ] 56 | }, 57 | { 58 | "login": "SaraVieira", 59 | "name": "Sara Vieira", 60 | "avatar_url": "https://avatars0.githubusercontent.com/u/1051509?v=4", 61 | "profile": "http://iamsaravieira.com", 62 | "contributions": [ 63 | "code", 64 | "ideas", 65 | "design", 66 | "doc" 67 | ] 68 | }, 69 | { 70 | "login": "farskid", 71 | "name": "Farzad YZ", 72 | "avatar_url": "https://avatars3.githubusercontent.com/u/8332043?v=4", 73 | "profile": "http://farzadyz.com", 74 | "contributions": [ 75 | "code", 76 | "bug", 77 | "ideas" 78 | ] 79 | }, 80 | { 81 | "login": "tsiq-swyx", 82 | "name": "tsiq-swyx", 83 | "avatar_url": "https://avatars0.githubusercontent.com/u/35976578?v=4", 84 | "profile": "https://github.com/tsiq-swyx", 85 | "contributions": [ 86 | "question", 87 | "ideas", 88 | "review" 89 | ] 90 | }, 91 | { 92 | "login": "siddharthkp", 93 | "name": "Siddharth Kshetrapal", 94 | "avatar_url": "https://avatars0.githubusercontent.com/u/1863771?v=4", 95 | "profile": "https://siddharthkp.github.io", 96 | "contributions": [ 97 | "code" 98 | ] 99 | }, 100 | { 101 | "login": "Shriram-Balaji", 102 | "name": "Shriram Balaji", 103 | "avatar_url": "https://avatars1.githubusercontent.com/u/11358903?v=4", 104 | "profile": "https://shriram-balaji.github.io", 105 | "contributions": [ 106 | "code" 107 | ] 108 | }, 109 | { 110 | "login": "sudovijay", 111 | "name": "Vijay Singh", 112 | "avatar_url": "https://avatars3.githubusercontent.com/u/4334983?v=4", 113 | "profile": "http://sudovijay.com", 114 | "contributions": [ 115 | "question", 116 | "code", 117 | "ideas" 118 | ] 119 | }, 120 | { 121 | "login": "scottaohara", 122 | "name": "Scott O'Hara", 123 | "avatar_url": "https://avatars3.githubusercontent.com/u/4152514?v=4", 124 | "profile": "https://scottohara.me", 125 | "contributions": [ 126 | "code" 127 | ] 128 | }, 129 | { 130 | "login": "cbh6", 131 | "name": "Cristian Botella", 132 | "avatar_url": "https://avatars1.githubusercontent.com/u/7105710?v=4", 133 | "profile": "https://github.com/cbh6", 134 | "contributions": [ 135 | "code" 136 | ] 137 | }, 138 | { 139 | "login": "vsashyn", 140 | "name": "Vitalii Sashyn", 141 | "avatar_url": "https://avatars2.githubusercontent.com/u/4557935?v=4", 142 | "profile": "https://github.com/vsashyn", 143 | "contributions": [ 144 | "code", 145 | "design" 146 | ] 147 | }, 148 | { 149 | "login": "vladamx", 150 | "name": "Vladimir Milojevic", 151 | "avatar_url": "https://avatars2.githubusercontent.com/u/11381171?v=4", 152 | "profile": "https://github.com/vladamx", 153 | "contributions": [ 154 | "bug", 155 | "code" 156 | ] 157 | }, 158 | { 159 | "login": "phoulgaux", 160 | "name": "Piotr Balbier", 161 | "avatar_url": "https://avatars1.githubusercontent.com/u/7175670?v=4", 162 | "profile": "https://github.com/phoulgaux", 163 | "contributions": [ 164 | "bug", 165 | "code" 166 | ] 167 | }, 168 | { 169 | "login": "shyamks", 170 | "name": "Meghashyam Kodmad", 171 | "avatar_url": "https://avatars1.githubusercontent.com/u/7412845?v=4", 172 | "profile": "https://github.com/shyamks", 173 | "contributions": [ 174 | "code" 175 | ] 176 | } 177 | ] 178 | } 179 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["razzle/babel", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": ["standard", "plugin:react/recommended"], 4 | "rules": { 5 | "indent": [0, 4], 6 | "react/react-in-jsx-scope": 0, 7 | "react/prop-types": 0, 8 | "react/display-name": 0, 9 | "space-before-function-paren": 0 10 | }, 11 | "settings": { 12 | "react": { 13 | "version": "16.0.4" 14 | } 15 | }, 16 | "globals": { 17 | "localStorage": true, 18 | "fetch": true, 19 | "test": true, 20 | "expect": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | gh-pages 3 | 4 | # Esdoc dirs 5 | out 6 | gh-pages 7 | 8 | dist 9 | build 10 | 11 | # Docker 12 | *.dockerfile 13 | *.dockerignore 14 | 15 | # NPM config 16 | .npmrc 17 | 18 | # Created by https://www.gitignore.io/api/node,windows,osx,linux,vim 19 | 20 | ### Linux ### 21 | *~ 22 | 23 | # temporary files which can be created if a process still has a handle open of a deleted file 24 | .fuse_hidden* 25 | 26 | # KDE directory preferences 27 | .directory 28 | 29 | # Linux trash folder which might appear on any partition or disk 30 | .Trash-* 31 | 32 | # .nfs files are created when an open file is removed but is still being accessed 33 | .nfs* 34 | 35 | ### Node ### 36 | # Logs 37 | logs 38 | *.log 39 | npm-debug.log* 40 | yarn-debug.log* 41 | yarn-error.log* 42 | 43 | # Runtime data 44 | pids 45 | *.pid 46 | *.seed 47 | *.pid.lock 48 | 49 | # Directory for instrumented libs generated by jscoverage/JSCover 50 | lib-cov 51 | 52 | # Coverage directory used by tools like istanbul 53 | coverage 54 | 55 | # nyc test coverage 56 | .nyc_output 57 | 58 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 59 | .grunt 60 | 61 | # Bower dependency directory (https://bower.io/) 62 | bower_components 63 | 64 | # node-waf configuration 65 | .lock-wscript 66 | 67 | # Compiled binary addons (http://nodejs.org/api/addons.html) 68 | build/Release 69 | 70 | # Dependency directories 71 | node_modules/ 72 | jspm_packages/ 73 | 74 | # Typescript v1 declaration files 75 | typings/ 76 | 77 | # Optional npm cache directory 78 | .npm 79 | 80 | # Optional eslint cache 81 | .eslintcache 82 | 83 | # Optional REPL history 84 | .node_repl_history 85 | 86 | # Output of 'npm pack' 87 | *.tgz 88 | 89 | # Yarn Integrity file 90 | .yarn-integrity 91 | 92 | # dotenv environment variables file 93 | .env 94 | 95 | 96 | ### OSX ### 97 | *.DS_Store 98 | .AppleDouble 99 | .LSOverride 100 | 101 | # Icon must end with two \r 102 | Icon 103 | 104 | 105 | # Thumbnails 106 | ._* 107 | 108 | # Files that might appear in the root of a volume 109 | .DocumentRevisions-V100 110 | .fseventsd 111 | .Spotlight-V100 112 | .TemporaryItems 113 | .Trashes 114 | .VolumeIcon.icns 115 | .com.apple.timemachine.donotpresent 116 | 117 | # Directories potentially created on remote AFP share 118 | .AppleDB 119 | .AppleDesktop 120 | Network Trash Folder 121 | Temporary Items 122 | .apdisk 123 | 124 | ### Vim ### 125 | # swap 126 | [._]*.s[a-v][a-z] 127 | [._]*.sw[a-p] 128 | [._]s[a-v][a-z] 129 | [._]sw[a-p] 130 | # session 131 | Session.vim 132 | # temporary 133 | .netrwhist 134 | # auto-generated tag files 135 | tags 136 | 137 | ### Windows ### 138 | # Windows thumbnail cache files 139 | Thumbs.db 140 | ehthumbs.db 141 | ehthumbs_vista.db 142 | 143 | # Folder config file 144 | Desktop.ini 145 | 146 | # Recycle Bin used on file shares 147 | $RECYCLE.BIN/ 148 | 149 | # Windows Installer files 150 | *.cab 151 | *.msi 152 | *.msm 153 | *.msp 154 | 155 | # Windows shortcuts 156 | *.lnk 157 | # es-module.patch 158 | *.patch 159 | build 160 | 161 | # End of https://www.gitignore.io/api/node,windows,osx,linux,vim 162 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "tabWidth": 4, 5 | "overrides": [ 6 | { 7 | "files": "*.md", 8 | "options": { 9 | "printWidth": 70, 10 | "useTabs": false, 11 | "trailingComma": "none", 12 | "proseWrap": "never" 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure } from '@storybook/react' 2 | 3 | // automatically import all files ending in *.stories.js 4 | const req = require.context('../src', true, /.story.js$/) 5 | function loadStories() { 6 | req.keys().forEach(filename => req(filename)) 7 | } 8 | 9 | configure(loadStories, module) 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | directories: 4 | - node_modules 5 | yarn: true 6 | node_js: 7 | - 9 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | 3 | Copyright 2018 Sara Vieira, contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Awesome Talks 2 | 3 | This website is no longer online and for that the repo is archived. It should still work locally even though the packages are super outdated. 4 | 5 | If you want to start a new project the data that was on this website is in https://github.com/sw-yx/awesometalksdata 6 | 7 | 8 | ## What was it ? 9 | 10 | Awesome Tech Talks Curated by the Community 11 | 12 | ## How to Run it ? 13 | 14 | First you need node and git installed after that run: 15 | 16 | ```sh 17 | git clone https://github.com/SaraVieira/awesome-talks 18 | cd awesome-talks 19 | yarn 20 | yarn dev 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /add-duration/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | node_modules 3 | -------------------------------------------------------------------------------- /add-duration/index.js: -------------------------------------------------------------------------------- 1 | const { json } = require('micro') 2 | const { request } = require('graphql-request') 3 | const getYoutube = require('./youtube') 4 | require('dotenv').config() 5 | const endpoint = 'https://api.graphcms.com/simple/v1/awesometalks' 6 | 7 | const updateDuration = ` 8 | mutation updateVideos($id: ID!, $duration: Int, $year: Int, $likes: Int, $views: Int) { 9 | updateVideos(id: $id, duration: $duration, year: $year, likes: $likes, views: $views) { 10 | id, 11 | duration, 12 | year, 13 | views, 14 | likes 15 | } 16 | } 17 | ` 18 | // const getVideos = ` 19 | // query getVideos { 20 | // allVideoses { 21 | // id, link 22 | // } 23 | // } 24 | // ` 25 | module.exports = async req => { 26 | // const allVideos = await request(endpoint, getVideos) 27 | // const videos = allVideos.allVideoses.reverse() 28 | // videos.map(async ({ link, id }) => { 29 | // const youtube = await getYoutube(link) 30 | // try { 31 | // await request(endpoint, updateDuration, { 32 | // id: id, 33 | // duration: youtube.duration, 34 | // year: youtube.year, 35 | // likes: youtube.likes, 36 | // views: youtube.views 37 | // }) 38 | // } catch (e) { 39 | // console.log(e) 40 | // } 41 | // }) 42 | const js = await json(req) 43 | const video = js.data.Videos.node 44 | try { 45 | const youtube = await getYoutube(video.link) 46 | try { 47 | const update = await request(endpoint, updateDuration, { 48 | id: video.id, 49 | duration: youtube.duration, 50 | year: youtube.year 51 | }) 52 | console.log(update) 53 | } catch (e) { 54 | console.log(e) 55 | } 56 | } catch (e) { 57 | console.log(e) 58 | } 59 | return null 60 | } 61 | -------------------------------------------------------------------------------- /add-duration/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awesome-talks-duration-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "micro" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "dotenv": "^6.0.0", 14 | "graphql-request": "^1.6.0", 15 | "micro": "^9.3.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /add-duration/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | ansi-styles@^3.2.1: 6 | version "3.2.1" 7 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 8 | dependencies: 9 | color-convert "^1.9.0" 10 | 11 | arg@2.0.0: 12 | version "2.0.0" 13 | resolved "https://registry.yarnpkg.com/arg/-/arg-2.0.0.tgz#c06e7ff69ab05b3a4a03ebe0407fac4cba657545" 14 | 15 | bytes@3.0.0: 16 | version "3.0.0" 17 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" 18 | 19 | chalk@2.4.0: 20 | version "2.4.0" 21 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.0.tgz#a060a297a6b57e15b61ca63ce84995daa0fe6e52" 22 | dependencies: 23 | ansi-styles "^3.2.1" 24 | escape-string-regexp "^1.0.5" 25 | supports-color "^5.3.0" 26 | 27 | color-convert@^1.9.0: 28 | version "1.9.1" 29 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" 30 | dependencies: 31 | color-name "^1.1.1" 32 | 33 | color-name@^1.1.1: 34 | version "1.1.3" 35 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 36 | 37 | content-type@1.0.4: 38 | version "1.0.4" 39 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" 40 | 41 | cross-fetch@2.0.0: 42 | version "2.0.0" 43 | resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.0.0.tgz#a17475449561e0f325146cea636a8619efb9b382" 44 | dependencies: 45 | node-fetch "2.0.0" 46 | whatwg-fetch "2.0.3" 47 | 48 | depd@1.1.1: 49 | version "1.1.1" 50 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" 51 | 52 | dotenv@^6.0.0: 53 | version "6.0.0" 54 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.0.0.tgz#24e37c041741c5f4b25324958ebbc34bca965935" 55 | 56 | escape-string-regexp@^1.0.5: 57 | version "1.0.5" 58 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 59 | 60 | graphql-request@^1.6.0: 61 | version "1.6.0" 62 | resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-1.6.0.tgz#afe87cf2a336acabb0cc2a875900202eda89f412" 63 | dependencies: 64 | cross-fetch "2.0.0" 65 | 66 | has-flag@^3.0.0: 67 | version "3.0.0" 68 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 69 | 70 | http-errors@1.6.2: 71 | version "1.6.2" 72 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" 73 | dependencies: 74 | depd "1.1.1" 75 | inherits "2.0.3" 76 | setprototypeof "1.0.3" 77 | statuses ">= 1.3.1 < 2" 78 | 79 | iconv-lite@0.4.19: 80 | version "0.4.19" 81 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" 82 | 83 | inherits@2.0.3: 84 | version "2.0.3" 85 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 86 | 87 | is-stream@1.1.0: 88 | version "1.1.0" 89 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 90 | 91 | micro@^9.3.2: 92 | version "9.3.2" 93 | resolved "https://registry.yarnpkg.com/micro/-/micro-9.3.2.tgz#41e249fe96a33db056f0a5dabb32c8fd4b69c5ca" 94 | dependencies: 95 | arg "2.0.0" 96 | chalk "2.4.0" 97 | content-type "1.0.4" 98 | is-stream "1.1.0" 99 | raw-body "2.3.2" 100 | 101 | node-fetch@2.0.0: 102 | version "2.0.0" 103 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.0.0.tgz#982bba43ecd4f2922a29cc186a6bbb0bb73fcba6" 104 | 105 | raw-body@2.3.2: 106 | version "2.3.2" 107 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" 108 | dependencies: 109 | bytes "3.0.0" 110 | http-errors "1.6.2" 111 | iconv-lite "0.4.19" 112 | unpipe "1.0.0" 113 | 114 | setprototypeof@1.0.3: 115 | version "1.0.3" 116 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" 117 | 118 | "statuses@>= 1.3.1 < 2": 119 | version "1.5.0" 120 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" 121 | 122 | supports-color@^5.3.0: 123 | version "5.4.0" 124 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" 125 | dependencies: 126 | has-flag "^3.0.0" 127 | 128 | unpipe@1.0.0: 129 | version "1.0.0" 130 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 131 | 132 | whatwg-fetch@2.0.3: 133 | version "2.0.3" 134 | resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" 135 | -------------------------------------------------------------------------------- /add-duration/youtube.js: -------------------------------------------------------------------------------- 1 | const getDurationInSecond = input => { 2 | const reptms = /^PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$/ 3 | let hours = 0 4 | let minutes = 0 5 | let seconds = 0 6 | let totalseconds 7 | 8 | if (reptms.test(input)) { 9 | var matches = reptms.exec(input) 10 | if (matches[1]) hours = Number(matches[1]) 11 | if (matches[2]) minutes = Number(matches[2]) 12 | if (matches[3]) seconds = Number(matches[3]) 13 | totalseconds = hours * 3600 + minutes * 60 + seconds 14 | } 15 | return totalseconds 16 | } 17 | module.exports = async id => { 18 | const getVideo = await fetch( 19 | `https://www.googleapis.com/youtube/v3/videos?id=${id}&key=${ 20 | process.env.KEY 21 | }&part=contentDetails,snippet,statistics` 22 | ) 23 | const rsp = await getVideo.json() 24 | const item = rsp.items[0] 25 | return { 26 | year: new Date(item.snippet.publishedAt).getFullYear(), 27 | duration: getDurationInSecond(item.contentDetails.duration), 28 | views: parseInt(item.statistics.viewCount, 10), 29 | likes: parseInt(item.statistics.likeCount, 10) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /bot/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | node_modules 3 | -------------------------------------------------------------------------------- /bot/index.js: -------------------------------------------------------------------------------- 1 | const { json } = require('micro') 2 | var TwitterPackage = require('twitter') 3 | require('dotenv').config() 4 | 5 | var secret = { 6 | consumer_key: process.env.CONSUMER_KEY, 7 | consumer_secret: process.env.CONSUMER_SECRET, 8 | access_token_key: process.env.ACCESS_KEY, 9 | access_token_secret: process.env.ACCESS_SECRET 10 | } 11 | var Twitter = new TwitterPackage(secret) 12 | 13 | module.exports = async (req, res) => { 14 | const js = await json(req) 15 | const newVideo = js.data.Videos.node 16 | const speaker = newVideo.speaker[0] 17 | 18 | const twitter = speaker.twitter.includes('.com/') 19 | ? speaker.twitter.split('.com/')[1] 20 | : speaker.twitter 21 | 22 | const Tags = newVideo.tags.map(tag => `#${tag.name}`).join(' ') 23 | 24 | /** 25 | * {"speaker[0]":[{"name":"Heydon Pickering","twitter":"heydonworks"}],"name":"Writing Less Damned Code","tags":[{"name":"UX"},{"name":"Accessibility"}],"isPublished":true,"id":"cjhq1jcdk1uwg0104bdwylkrg","link":"tzfHlEFd2Fk"} 26 | * 27 | */ 28 | 29 | if (newVideo.isPublished) { 30 | Twitter.post( 31 | 'statuses/update', 32 | { 33 | status: `New Talk Release 🎉: ${newVideo.name} by the awesome ${ 34 | speaker.name 35 | }(@${twitter}) - https://youtube.com/watch?v=${newVideo.link} 36 | ${Tags} 37 | ` 38 | }, 39 | function(error, tweet, response) { 40 | if (error) { 41 | console.log(error) 42 | } 43 | console.log(tweet) 44 | } 45 | ) 46 | } 47 | 48 | return null 49 | } 50 | -------------------------------------------------------------------------------- /bot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awesome-talks-twitter-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "micro" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "dotenv": "^6.0.0", 14 | "micro": "^9.3.2", 15 | "twitter": "^1.7.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SaraVieira/awesome-talks/7058aa2ba4a7102bce151630f397dd160fd12bb3/gh.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awesome-talks", 3 | "description": "Awesome Talks", 4 | "license": "MIT", 5 | "version": "0.0.1", 6 | "dependencies": { 7 | "@fortawesome/fontawesome": "^1.1.8", 8 | "@fortawesome/fontawesome-free": "^5.6.3", 9 | "@fortawesome/fontawesome-svg-core": "^1.2.12", 10 | "@fortawesome/free-solid-svg-icons": "^5.6.3", 11 | "@fortawesome/react-fontawesome": "^0.1.4", 12 | "apollo-boost": "0.1.27", 13 | "apollo-cache-inmemory": "1.4.2", 14 | "apollo-client": "2.4.12", 15 | "apollo-fetch": "^0.7.0", 16 | "apollo-link": "1.2.6", 17 | "apollo-link-http": "1.5.9", 18 | "apollo-link-state": "0.4.2", 19 | "card-vibes": "0.1.0", 20 | "cookie-parser": "^1.4.3", 21 | "eslint-config-react-app": "3.0.6", 22 | "express": "4.16.4", 23 | "formik": "1.4.2", 24 | "graphql": "14.1.1", 25 | "graphql-tag": "2.10.1", 26 | "history": "4.7.2", 27 | "isomorphic-fetch": "2.2.1", 28 | "js-cookie": "^2.2.0", 29 | "lodash.debounce": "4.0.8", 30 | "razzle": "2.4.1", 31 | "react": "16.7.0", 32 | "react-apollo": "2.4.0", 33 | "react-dom": "16.7.0", 34 | "react-fluid-carousel": "^0.7.0", 35 | "react-helmet": "5.2.0", 36 | "react-modal": "3.8.1", 37 | "react-router-dom": "4.3.1", 38 | "react-social-sharing": "^3.0.1", 39 | "react-styled-flexboxgrid": "3.0.0", 40 | "react-youtube": "7.9.0", 41 | "remcalc": "1.0.10", 42 | "rss": "^1.2.2", 43 | "shuffle-array": "1.0.1", 44 | "styled-components": "4.1.3", 45 | "styled-flex-component": "2.2.2", 46 | "styled-is": "1.1.5", 47 | "uuid": "^3.3.2" 48 | }, 49 | "devDependencies": { 50 | "@storybook/addon-actions": "4.1.7", 51 | "@storybook/addon-links": "4.1.7", 52 | "@storybook/addons": "4.1.7", 53 | "@storybook/react": "4.1.7", 54 | "all-contributors-cli": "5.10.2", 55 | "babel-cli": "^6.26.0", 56 | "babel-preset-react": "^6.24.1", 57 | "enzyme": "^3.8.0", 58 | "enzyme-adapter-react-16": "^1.7.1", 59 | "enzyme-to-json": "^3.3.5", 60 | "eslint": "5.12.1", 61 | "eslint-config-standard": "12.0.0", 62 | "eslint-plugin-import": "2.14.0", 63 | "eslint-plugin-jsx-a11y": "6.1.2", 64 | "eslint-plugin-node": "8.0.1", 65 | "eslint-plugin-promise": "4.0.1", 66 | "eslint-plugin-react": "7.12.4", 67 | "eslint-plugin-standard": "4.0.0", 68 | "husky": "1.3.1", 69 | "if-env": "1.0.4", 70 | "jest": "^23.6.0", 71 | "jest-cli": "^23.6.0", 72 | "jest-styled-components": "^6.3.1", 73 | "lint-staged": "8.1.0", 74 | "lodash-webpack-plugin": "0.11.5", 75 | "npm-run-all": "^4.1.5", 76 | "offline-plugin": "^5.0.6", 77 | "prettier": "1.15.3", 78 | "react-test-renderer": "^16.7.0", 79 | "sinon": "7.2.3", 80 | "webpack-bundle-analyzer": "3.0.3" 81 | }, 82 | "scripts": { 83 | "posttest": "npm run format", 84 | "start": "NODE_ENV=production node ./build/server.js", 85 | "dev": "razzle start", 86 | "build": "razzle build", 87 | "format": "prettier --write '**/*.{js,css,md}'", 88 | "lint": "eslint . --cache --fix", 89 | "test": "run-s lint", 90 | "precommit": "lint-staged", 91 | "jest": "jest", 92 | "contributors:add": "all-contributors add", 93 | "contributors:generate": "all-contributors generate", 94 | "storybook": "start-storybook -p 6006", 95 | "build-storybook": "build-storybook" 96 | }, 97 | "jest": { 98 | "snapshotSerializers": [ 99 | "enzyme-to-json/serializer" 100 | ], 101 | "setupFiles": [ 102 | "./src/Tests/setup.js" 103 | ] 104 | }, 105 | "now": { 106 | "alias": "awesometalks.party" 107 | }, 108 | "lint-staged": { 109 | "*.{js,css,md}": [ 110 | "prettier --write", 111 | "git add" 112 | ], 113 | "*.js": [ 114 | "eslint --fix", 115 | "git add", 116 | "jest --findRelatedTests" 117 | ] 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SaraVieira/awesome-talks/7058aa2ba4a7102bce151630f397dd160fd12bb3/public/favicon.ico -------------------------------------------------------------------------------- /razzle.config.js: -------------------------------------------------------------------------------- 1 | // const OfflinePlugin = require('offline-plugin') 2 | 3 | module.exports = { 4 | modify: (config, { target, dev }, webpack) => { 5 | // if (target === 'web') { 6 | // config.plugins.push( 7 | // new OfflinePlugin({ 8 | // relativePaths: false, 9 | // publicPath: '/', 10 | // caches: 'all', 11 | // externals: ['/'], 12 | // ServiceWorker: { 13 | // output: './sw.js', 14 | // navigateFallbackURL: '/' 15 | // } 16 | // }) 17 | // ) 18 | // } 19 | 20 | return config 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Route, Switch } from 'react-router-dom' 3 | import Home from './Pages/Home' 4 | import Speakers from './Pages/Speakers' 5 | import Proposed from './Pages/Proposed' 6 | import Speaker from './Pages/Speaker' 7 | import Tags from './Pages/Tags' 8 | import Tag from './Pages/Tag' 9 | import Favorites from './Pages/Favorites' 10 | import FourOFour from './Pages/FourOFour' 11 | import Video from './Pages/Video' 12 | 13 | import './Utils/icons' 14 | 15 | export default () => ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ) 28 | -------------------------------------------------------------------------------- /src/Components/CinemaMode/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { createPortal } from 'react-dom' 3 | import { Col } from 'react-styled-flexboxgrid' 4 | import remcalc from 'remcalc' 5 | import styled from 'styled-components' 6 | import is from 'styled-is' 7 | 8 | const Column = styled(Col)` 9 | transition: all 200ms ease; 10 | justify-content: center; 11 | margin: 0 auto; 12 | margin-bottom: ${remcalc(40)}; 13 | 14 | ${is('cinemaMode')` 15 | position: fixed; 16 | z-index: 9999; 17 | top: 10%; 18 | width: 90%; 19 | left: 50%; 20 | transform: translateX(-50%); 21 | background: ${props => props.theme.primary}; 22 | padding: ${remcalc(20)}; 23 | max-height: 90%; 24 | overflow: scroll; 25 | padding-bottom: ${remcalc(50)}; 26 | `}; 27 | ` 28 | 29 | const Button = styled.button` 30 | background: transparent; 31 | display: block; 32 | border: none; 33 | color: ${props => props.theme.cinema}; 34 | font-weight: 600; 35 | text-align: right; 36 | padding: 6px 0px; 37 | cursor: pointer; 38 | text-transform: uppercase; 39 | transition: background 200ms ease; 40 | ` 41 | 42 | const Overlay = styled.div` 43 | width: 100%; 44 | height: 100%; 45 | background: rgba(0, 0, 0, 0.9); 46 | position: fixed; 47 | top: 0; 48 | left: 0; 49 | z-index: 10; 50 | ` 51 | 52 | export default class CinemaMode extends Component { 53 | state = { 54 | cinemaMode: false, 55 | showVideo: false 56 | } 57 | 58 | handleKeyDown = event => { 59 | if ( 60 | event.keyCode === 27 && 61 | document.body.classList.contains('cinema-mode') 62 | ) { 63 | this.toggleCinemaMode() 64 | } 65 | } 66 | 67 | toggleCinemaMode = () => { 68 | this.setState( 69 | ({ cinemaMode }) => ({ 70 | cinemaMode: !cinemaMode, 71 | showVideo: !cinemaMode 72 | }), 73 | () => { 74 | document.body.classList.toggle( 75 | 'cinema-mode', 76 | this.state.cinemaMode 77 | ) 78 | 79 | if (this.state.cinemaMode === false) { 80 | document.removeEventListener('keydown', this.handleKeyDown) 81 | } else { 82 | document.addEventListener('keydown', this.handleKeyDown) 83 | } 84 | } 85 | ) 86 | } 87 | 88 | cinemaButton = () => { 89 | return ( 90 | 93 | ) 94 | } 95 | 96 | render() { 97 | const { render } = this.props 98 | const { cinemaMode, showVideo } = this.state 99 | return ( 100 | 106 |
107 | {render(cinemaMode, showVideo, this.toggleCinemaMode)} 108 | 109 | {this.cinemaButton()} 110 | 111 | {cinemaMode 112 | ? createPortal( 113 | , 114 | document.getElementsByTagName('body')[0] 115 | ) 116 | : null} 117 |
118 |
119 | ) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Components/CinemaMode/story.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { storiesOf } from '@storybook/react' 4 | 5 | import CinemaView from './index' 6 | 7 | storiesOf('CinemaView', module).add('Default', () => { 8 | return 9 | }) 10 | -------------------------------------------------------------------------------- /src/Components/Container.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Grid, Row, Col } from 'react-styled-flexboxgrid' 3 | 4 | export default ({ children }) => ( 5 | 6 | 7 | {children} 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /src/Components/CookieBanner.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import styled from 'styled-components' 3 | import { BANNER_KEY, getStorage, setStorage } from './../Utils/state' 4 | 5 | const Banner = styled.div` 6 | position: fixed; 7 | left: 0; 8 | bottom: 0; 9 | right: 0; 10 | background: rgb(238, 238, 238); 11 | color: rgb(51, 51, 51); 12 | height: auto; 13 | z-index: 1000; 14 | font-size: 16px; 15 | text-align: center; 16 | padding: 10px 18px; 17 | display: flex; 18 | justify-content: center; 19 | align-items: center; 20 | ` 21 | 22 | const BannerText = styled.div` 23 | line-height: 1.8; 24 | margin-right: 5px; 25 | ` 26 | const BannerClose = styled.div` 27 | float: right; 28 | display: block; 29 | padding: 5px 20px; 30 | margin-left: 5px; 31 | border-radius: 5px; 32 | cursor: pointer; 33 | color: rgb(0, 0, 0); 34 | background: rgb(241, 214, 0); 35 | text-align: center; 36 | flex-shrink: 0; 37 | ` 38 | 39 | class CookieBanner extends Component { 40 | state = { 41 | shown: false 42 | } 43 | 44 | handleClose = event => { 45 | this.setState( 46 | { 47 | shown: true 48 | }, 49 | () => { 50 | setStorage(BANNER_KEY, '1') 51 | } 52 | ) 53 | } 54 | 55 | render() { 56 | return getStorage(BANNER_KEY) || 57 | this.state.shown === true || 58 | typeof window === 'undefined' ? ( 59 | '' 60 | ) : ( 61 | 62 | 63 | We use cookies for favorites, watched and also check if you 64 | in dark / light mode. By continuing to visit this site you 65 | agree to our use of cookies. 66 | 67 | Got it! 68 | 69 | ) 70 | } 71 | } 72 | 73 | export default CookieBanner 74 | -------------------------------------------------------------------------------- /src/Components/Errors/Error404.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import remcalc from 'remcalc' 4 | import { Link } from 'react-router-dom' 5 | 6 | const Wrapper = styled.div` 7 | text-align: center; 8 | padding: ${remcalc(30)}; 9 | color: ${props => props.theme.main}; 10 | 11 | h1 { 12 | font-size: ${remcalc(30)}; 13 | margin-bottom: ${remcalc(30)}; 14 | } 15 | 16 | p { 17 | font-size: ${remcalc(18)}; 18 | margin: ${remcalc(20)} 0; 19 | } 20 | ` 21 | 22 | const Button = styled.div` 23 | margin-top: ${remcalc(40)}; 24 | a { 25 | border: 0; 26 | padding: ${remcalc(7)} ${remcalc(25)}; 27 | background: rgb(238, 238, 238); 28 | font-weight: 700; 29 | border-radius: 4px; 30 | text-transform: uppercase; 31 | color: #333; 32 | bos-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.12), 33 | 0 2px 4px 0 rgba(0, 0, 0, 0.08); 34 | 35 | &:hover { 36 | color: #555; 37 | } 38 | 39 | &:after { 40 | height: 0; 41 | background: none; 42 | } 43 | } 44 | ` 45 | 46 | export default () => ( 47 | 48 |

Oh No!

49 |

It looks like you are lost

50 |

The page {"you're"} looking for is not here

51 | 54 |
55 | ) 56 | -------------------------------------------------------------------------------- /src/Components/Favorite/example.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { action } from '@storybook/addon-actions' 3 | import Favorite from './view' 4 | 5 | const favorites = [1, 2, 3] 6 | 7 | export const Favorited = () => { 8 | return ( 9 | 15 | ) 16 | } 17 | 18 | export const UnFavorited = () => ( 19 | 25 | ) 26 | -------------------------------------------------------------------------------- /src/Components/Favorite/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { graphql, compose } from 'react-apollo' 3 | import GET_FAVORITES from '../../Queries/Local/GET_FAVORITES' 4 | import ADD_FAVORITE from '../../Queries/Local/ADD_FAVORITE' 5 | import REMOVE_FAVORITE from '../../Queries/Local/REMOVE_FAVORITE' 6 | import Query from './../Query' 7 | import View from './view' 8 | 9 | const Favorite = props => ( 10 | 11 | {({ data: { favorites } }) => { 12 | return 13 | }} 14 | 15 | ) 16 | 17 | export default compose( 18 | graphql(REMOVE_FAVORITE, { 19 | props: ({ mutate }) => ({ 20 | removeFavorite: id => mutate({ variables: { id } }) 21 | }) 22 | }), 23 | graphql(ADD_FAVORITE, { 24 | props: ({ mutate }) => ({ 25 | addFavorite: id => mutate({ variables: { id } }) 26 | }) 27 | }) 28 | )(Favorite) 29 | -------------------------------------------------------------------------------- /src/Components/Favorite/story.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { storiesOf } from '@storybook/react' 4 | 5 | import { Favorited, UnFavorited } from './example' 6 | 7 | storiesOf('Favorite', module) 8 | .add('Favorited', () => { 9 | return 10 | }) 11 | .add('Unfavorited', () => { 12 | return 13 | }) 14 | -------------------------------------------------------------------------------- /src/Components/Favorite/view.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Heart from '../Styling/Heart' 3 | 4 | import uuidV4 from 'uuid/v4' 5 | 6 | export default ({ favorites, removeFavorite, addFavorite, id, big }) => { 7 | const inputId = uuidV4() 8 | return ( 9 | 10 | 15 | favorites.includes(id) 16 | ? removeFavorite(id) 17 | : addFavorite(id) 18 | } 19 | /> 20 | 25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /src/Components/Filters/Duration.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Flex from 'styled-flex-component' 3 | import { Name, Button } from '../Styling/Text' 4 | 5 | export default ({ onClick, duration }) => ( 6 | 7 | Duration 8 | 20 | 32 | 44 | 45 | ) 46 | -------------------------------------------------------------------------------- /src/Components/Filters/Order.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Flex from 'styled-flex-component' 3 | import { Name } from '../Styling/Text' 4 | import { Select } from '../Styling/Forms' 5 | 6 | export default ({ onChange }) => ( 7 | 8 | Order 9 | 23 | 24 | ) 25 | -------------------------------------------------------------------------------- /src/Components/Filters/Year.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Flex from 'styled-flex-component' 3 | import { Name, Button } from '../Styling/Text' 4 | 5 | export default ({ onClick, year }) => ( 6 | 7 | Published year 8 | 13 | 18 | 23 | 28 | 29 | 30 | ) 31 | 32 | const PublishedYearButton = ({ value, onClick, year }) => ( 33 | 45 | ) 46 | -------------------------------------------------------------------------------- /src/Components/Header/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { Row, Col } from 'react-styled-flexboxgrid' 4 | 5 | import Flex from 'styled-flex-component' 6 | import remcalc from 'remcalc' 7 | import is from 'styled-is' 8 | import Search from '../Search' 9 | import { withRouter } from 'react-router' 10 | import HideViewed from '../HideViewed' 11 | 12 | export const Title = styled.h1` 13 | opacity: 0.8; 14 | font-family: Avenir, Montserrat; 15 | font-weight: 600; 16 | font-size: ${remcalc(90)}; 17 | line-height: 1.2; 18 | color: ${props => props.theme.main}; 19 | letter-spacing: ${remcalc(-0.25)}; 20 | margin-top: 0; 21 | margin-bottom: 0; 22 | text-transform: capitalize; 23 | ${is('code')` 24 | font-family: 'Space Mono', monospace; 25 | `}; 26 | 27 | ${is('medium')` 28 | font-size: ${remcalc(70)}; 29 | 30 | @media (max-width: ${remcalc(768)}) { 31 | margin-bottom: ${remcalc(48)}; 32 | } 33 | `}; 34 | 35 | ${is('small')` 36 | font-size: ${remcalc(24)}; 37 | 38 | @media (max-width: ${remcalc(768)}) { 39 | margin-bottom: ${remcalc(20)}; 40 | } 41 | `}; 42 | 43 | @media (max-width: ${remcalc(768)}) { 44 | font-size: ${remcalc(30)}; 45 | position: relative; 46 | margin: auto; 47 | margin-bottom: ${remcalc(40)}; 48 | margin-top: ${remcalc(-20)}; 49 | } 50 | ` 51 | 52 | const SearchWrapper = styled(Flex)` 53 | @media (max-width: ${remcalc(768)}) { 54 | flex-direction: column; 55 | margin-bottom: ${remcalc(30)}; 56 | justify-content: flex-start; 57 | 58 | h1 { 59 | margin-bottom: 0; 60 | } 61 | } 62 | ` 63 | 64 | const Wrapper = styled(Row)` 65 | margin-bottom: ${remcalc(60)}; 66 | 67 | ${is('noMargin')` 68 | margin-bottom: ${remcalc(0)}; 69 | `}; 70 | 71 | @media (max-width: ${remcalc(768)}) { 72 | margin: auto; 73 | margin-bottom: ${remcalc(20)}; 74 | } 75 | 76 | ${is('small')` 77 | margin-bottom: ${remcalc(20)}; 78 | `}; 79 | ` 80 | 81 | const Header = ({ 82 | title = 'Talks', 83 | noSearch, 84 | small, 85 | match, 86 | keyName, 87 | query, 88 | HideViewed, 89 | code, 90 | noMargin, 91 | medium 92 | }) => ( 93 | 94 | 95 | 96 | 97 | {title} 98 | 99 | {noSearch ? null : } 100 | 101 | {match.path === '/' ? : null} 102 | 103 | 104 | ) 105 | 106 | export default withRouter(props => ( 107 |
108 | )) 109 | -------------------------------------------------------------------------------- /src/Components/HideViewed/example.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { action } from '@storybook/addon-actions' 3 | import HideViewed from './view' 4 | 5 | const client = { 6 | writeData: action('Save hideviewed') 7 | } 8 | 9 | export const NotHidden = () => { 10 | return 11 | } 12 | 13 | export const Hidden = () => { 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /src/Components/HideViewed/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import SHOW_VIEWED from '../../Queries/Local/SHOW_VIEWED' 3 | import { Query } from 'react-apollo' 4 | import HideViewed from './view' 5 | 6 | export default () => ( 7 | 8 | {({ data: { hideViewed }, client }) => ( 9 | 10 | )} 11 | 12 | ) 13 | -------------------------------------------------------------------------------- /src/Components/HideViewed/story.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { storiesOf } from '@storybook/react' 4 | 5 | import { Hidden, NotHidden } from './example' 6 | 7 | storiesOf('HideViewed', module) 8 | .add('Hidden', () => { 9 | return 10 | }) 11 | .add('Not Hidden', () => { 12 | return 13 | }) 14 | -------------------------------------------------------------------------------- /src/Components/HideViewed/view.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import remcalc from 'remcalc' 4 | 5 | const Section = styled.div` 6 | display: flex; 7 | 8 | @media (max-width: ${remcalc(768)}) { 9 | top: ${remcalc(-55)}; 10 | position: relative; 11 | margin: auto; 12 | justify-content: center; 13 | } 14 | ` 15 | 16 | const Text = styled.div` 17 | font-size: 22px; 18 | padding: 4px; 19 | padding-left: 12px; 20 | font-weight: 200; 21 | color: ${props => props.theme.main}; 22 | ` 23 | 24 | const Hide = styled.div` 25 | .tgl { 26 | display: none; 27 | 28 | // add default box-sizing for this scope 29 | &, 30 | &:after, 31 | &:before, 32 | & *, 33 | & *:after, 34 | & *:before, 35 | & + .tgl-btn { 36 | box-sizing: border-box; 37 | &::selection { 38 | background: none; 39 | } 40 | } 41 | 42 | + .tgl-btn { 43 | outline: 0; 44 | display: block; 45 | width: 4em; 46 | height: 2em; 47 | position: relative; 48 | cursor: pointer; 49 | user-select: none; 50 | &:after, 51 | &:before { 52 | position: relative; 53 | display: block; 54 | content: ''; 55 | width: 50%; 56 | height: 100%; 57 | } 58 | 59 | &:after { 60 | left: 0; 61 | } 62 | 63 | &:before { 64 | display: none; 65 | } 66 | } 67 | 68 | &:checked + .tgl-btn:after { 69 | left: 50%; 70 | } 71 | } 72 | 73 | // themes 74 | 75 | .tgl-ios { 76 | + .tgl-btn { 77 | background: ${props => props.theme.lightGrey}; 78 | border-radius: 2em; 79 | transition: all 0.4s ease; 80 | &:after { 81 | border-radius: 2em; 82 | background: #fff; 83 | transition: left 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), 84 | padding 0.3s ease, margin 0.3s ease; 85 | box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1), 86 | 0 4px 0 rgba(0, 0, 0, 0.08); 87 | } 88 | 89 | &:hover:after { 90 | will-change: padding; 91 | } 92 | 93 | &:active { 94 | box-shadow: inset 0 0 0 2em #e8eae9; 95 | &:after { 96 | padding-right: 0.8em; 97 | } 98 | } 99 | } 100 | 101 | &:checked + .tgl-btn { 102 | background: ${props => props.theme.green}; 103 | &:active { 104 | box-shadow: none; 105 | &:after { 106 | margin-left: -0.8em; 107 | } 108 | } 109 | } 110 | } 111 | ` 112 | 113 | export default ({ hideViewed, client }) => { 114 | return ( 115 |
116 | 117 | 123 | client.writeData({ 124 | data: { hideViewed: !hideViewed } 125 | }) 126 | } 127 | checked={hideViewed} 128 | /> 129 | 131 | Hide Watched Talks 132 |
133 | ) 134 | } 135 | -------------------------------------------------------------------------------- /src/Components/MetaTags/FourOfFour.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Helmet } from 'react-helmet' 3 | 4 | export default () => ( 5 | 6 | 404 Page Not found! - Awesome Talks 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /src/Components/MetaTags/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Helmet } from 'react-helmet' 3 | 4 | export default () => ( 5 | 6 | Awesome Talks 7 | 11 | 12 | 16 | 17 | 18 | 19 | ) 20 | -------------------------------------------------------------------------------- /src/Components/MetaTags/Speaker.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Helmet } from 'react-helmet' 3 | 4 | export default ({ name, photo }) => ( 5 | 6 | Awesome Talks - {name} 7 | 8 | 9 | 10 | 11 | 15 | 16 | ) 17 | -------------------------------------------------------------------------------- /src/Components/MetaTags/Speakers.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Helmet } from 'react-helmet' 3 | 4 | export default () => ( 5 | 6 | Awesome Talks - Speakers 7 | 11 | 12 | 16 | 17 | 18 | 19 | ) 20 | -------------------------------------------------------------------------------- /src/Components/MetaTags/Tag.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Helmet } from 'react-helmet' 3 | import humanize from '../../Utils/strings' 4 | 5 | export default ({ category }) => ( 6 | 7 | Awesome Talks - {category} 8 | 12 | 16 | 20 | 21 | 22 | 23 | ) 24 | -------------------------------------------------------------------------------- /src/Components/MetaTags/Tags.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Helmet } from 'react-helmet' 3 | 4 | export default () => ( 5 | 6 | Awesome Talks - Categories 7 | 11 | 12 | 16 | 17 | 18 | 19 | ) 20 | -------------------------------------------------------------------------------- /src/Components/MetaTags/Video.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Helmet } from 'react-helmet' 3 | 4 | export default ({ name, link, description }) => ( 5 | 6 | Awesome Talks - {name} 7 | 11 | 12 | 13 | 14 | 18 | 19 | ) 20 | -------------------------------------------------------------------------------- /src/Components/Nav.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import styled from 'styled-components' 3 | import is from 'styled-is' 4 | import { Link } from 'react-router-dom' 5 | import remcalc from 'remcalc' 6 | import { Query } from 'react-apollo' 7 | 8 | import Logo from '../assets/logo.svg' 9 | import AddTalk from './AddTalk' 10 | import Container from './Container' 11 | import ToggleMode from './ToggleMode' 12 | import GET_FAVORITES from '../Queries/Local/GET_FAVORITES' 13 | 14 | import linkParser from '../Utils/link-parser' 15 | 16 | const Nav = styled.nav` 17 | display: flex; 18 | align-items: center; 19 | justify-content: space-between; 20 | padding: ${remcalc(40)} 0; 21 | 22 | @media (max-width: ${remcalc(768)}) { 23 | flex-direction: column; 24 | padding: ${remcalc(40)} ${remcalc(20)}; 25 | } 26 | ` 27 | 28 | const List = styled.ul` 29 | display: flex; 30 | justify-content: flex-end; 31 | 32 | @media (max-width: ${remcalc(768)}) { 33 | justify-content: center; 34 | flex-wrap: wrap; 35 | line-height: ${remcalc(40)}; 36 | } 37 | ` 38 | 39 | const LogoWrapper = styled(Link)` 40 | opacity: 1; 41 | border: none; 42 | &:after { 43 | display: none; 44 | } 45 | ` 46 | 47 | const Item = styled.li` 48 | @media (max-width: ${remcalc(768)}) { 49 | font-size: ${remcalc(16)}; 50 | 51 | ${is('hideOnMobile')` 52 | display: none; 53 | `}; 54 | } 55 | 56 | &:not(:last-child) { 57 | margin-right: ${remcalc(10)}; 58 | } 59 | ` 60 | 61 | export default class Navigation extends Component { 62 | state = { 63 | modalIsOpen: false, 64 | submitted: false, 65 | submitError: false 66 | } 67 | 68 | openModal = () => { 69 | this.setState({ modalIsOpen: true }) 70 | } 71 | 72 | closeModal = () => { 73 | this.setState({ modalIsOpen: false }) 74 | } 75 | 76 | handleError = errorMsg => { 77 | this.setState({ 78 | submitError: errorMsg 79 | }) 80 | this.props.handleReset() 81 | setTimeout(() => { 82 | this.setState({ 83 | submitError: false 84 | }) 85 | }, 3000) 86 | } 87 | 88 | submit = async (e, createVideos, values, setSubmitting, handleReset) => { 89 | e.preventDefault() 90 | 91 | // attach it 92 | this.handleReset = handleReset 93 | 94 | if (values.name.trim() === '' || values.link.trim() === '') { 95 | this.handleError('You must fill in all of the fields') 96 | return false 97 | } 98 | 99 | const link = linkParser(values.link) 100 | 101 | if (link.length !== 11) { 102 | this.handleError(link) 103 | return false 104 | } 105 | 106 | // remove multiple spaces from name 107 | values.name = String(values.name) 108 | .replace(/\s{2,}/gu, ' ') 109 | .trim() 110 | 111 | const valuesToBeSaved = { 112 | ...values, 113 | link 114 | } 115 | 116 | try { 117 | await createVideos({ variables: { ...valuesToBeSaved } }) 118 | } catch (err) { 119 | const msg = err.message.includes('A unique constraint') 120 | ? 'Awesome! We already have this. Thanks anyway.' 121 | : err.message 122 | 123 | this.handleError(msg) 124 | return false 125 | } 126 | 127 | setSubmitting(false) 128 | handleReset() 129 | this.setState({ submitted: true }, () => { 130 | setTimeout(() => { 131 | this.setState({ 132 | submitted: false 133 | }) 134 | }, 3000) 135 | }) 136 | } 137 | 138 | render = () => { 139 | return ( 140 | 141 | 202 | 203 | ) 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/Components/Player/example.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Favorited } from '../Favorite/example' 3 | import { Player } from './index' 4 | import { action } from '@storybook/addon-actions' 5 | 6 | export const ThumbnailPlayer = () => { 7 | const props = { 8 | cinemaMode: false, 9 | id: 1, 10 | link: `i2iCyulbnus`, 11 | showVideo: false, 12 | name: 'Live and Machine Learn', 13 | onClick: action('Thumbnail clicked'), 14 | onEnd: action('Video ended') 15 | } 16 | return null} {...props} /> 17 | } 18 | 19 | export const YoutubePlayer = () => { 20 | const props = { 21 | cinemaMode: false, 22 | id: 1, 23 | link: `i2iCyulbnus`, 24 | showVideo: true, 25 | name: 'Live and Machine Learn', 26 | onClick: action('Thumbnail clicked'), 27 | onEnd: action('Video ended') 28 | } 29 | return null} {...props} /> 30 | } 31 | -------------------------------------------------------------------------------- /src/Components/Player/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import YouTube from 'react-youtube' 4 | import is, { isNot } from 'styled-is' 5 | import remcalc from 'remcalc' 6 | import Card from 'card-vibes' 7 | 8 | import Favorite from '../Favorite' 9 | import Play from '../Styling/Play' 10 | import Watched from '../Watched' 11 | 12 | const Video = styled.div`` 13 | 14 | const VideoWrapper = styled.section` 15 | width: 100%; 16 | height: 100%; 17 | position: relative; 18 | margin: auto; 19 | ${isNot('cinemaMode')` 20 | &:before { 21 | display: block; 22 | content: ''; 23 | width: 100%; 24 | padding-top: 56.25%; 25 | } 26 | ${Video} { 27 | position: absolute; 28 | top: 0; 29 | left: 0; 30 | right: 0; 31 | bottom: 0; 32 | } 33 | `}; 34 | ` 35 | 36 | const Iframe = styled(YouTube)` 37 | position: relative; 38 | z-index: 3; 39 | border: none; 40 | transition: all 200ms ease; 41 | box-shadow: ${props => props.theme.shadow}; 42 | height: 100%; 43 | height: ${remcalc(231)}; 44 | ${is('cinemaMode')` 45 | height: ${remcalc(600)}; 46 | @media (max-width: ${remcalc(768)}) { 47 | height: auto; 48 | } 49 | `}; 50 | ` 51 | 52 | const Thumbnail = styled.img` 53 | display: block; 54 | max-width: 100%; 55 | cursor: pointer; 56 | ${isNot('videoPage')` 57 | // this hides the black bar on the thumbnail 58 | margin-top: -37px; 59 | `}; 60 | ` 61 | 62 | const Image = styled.div` 63 | position: relative; 64 | margin: auto; 65 | height: 100%; 66 | overflow: hidden; 67 | box-shadow: ${props => props.theme.shadow}; 68 | ${is('cinemaMode')` 69 | height: auto; 70 | `}; 71 | ` 72 | const Player = ({ 73 | cinemaMode, 74 | id, 75 | link, 76 | showVideo, 77 | name, 78 | onClick, 79 | onEnd, 80 | toggleCinemaMode, 81 | hq, 82 | videoPage 83 | }) => ( 84 | 85 |