├── .eslintrc.js ├── .github └── workflows │ ├── build.yml │ └── deploy.yml ├── .gitignore ├── .prettierrc ├── .travis.yml ├── LICENSE ├── README.md ├── gatsby-config.js ├── gatsby-node.js ├── package.json ├── postcss.config.js ├── site.webpack.js ├── src ├── assets │ ├── byteconf-full.png │ └── byteconf-no-bg.png ├── components │ ├── CTA │ │ ├── bg.jpg │ │ └── index.js │ ├── Event.js │ ├── Event │ │ ├── Header.js │ │ ├── Hero.js │ │ ├── Organizers.js │ │ ├── Schedule.js │ │ ├── Speakers.js │ │ ├── Sponsors.js │ │ ├── illustration.css │ │ └── illustration.svg │ ├── Events.js │ ├── Footer.js │ ├── Form.js │ ├── HomepageHero │ │ └── index.js │ ├── Layout.css │ ├── Layout.js │ ├── MdxLayout.js │ ├── Nav │ │ ├── index.js │ │ └── nav.png │ ├── Post.js │ ├── Posts.js │ ├── RelativeTime.js │ ├── SSRWrapper.js │ ├── Share.js │ ├── Social.js │ ├── Speaker.js │ └── Talk │ │ ├── Level.js │ │ ├── index.css │ │ └── index.js ├── pages │ ├── 404.js │ ├── about.mdx │ ├── blog.js │ ├── giveaway.mdx │ ├── index.js │ ├── merry.js │ └── terms.mdx ├── system.js ├── templates │ ├── blogPost.js │ ├── blogPost.scss │ ├── event.css │ ├── event.js │ ├── eventRSVP.js │ └── tag.js └── utils │ └── s3Url.js ├── static ├── OneSignalSDKUpdaterWorker.js ├── OneSignalSDKWorker.js ├── bytesized-social.png ├── favicon.ico ├── manifest.json ├── oneSignal.js └── robots.txt ├── tailwind.config.js ├── workers-site ├── .cargo-ok ├── .gitignore ├── gh-release.js ├── index.js ├── package-lock.json ├── package.json └── templates │ ├── sponsor_prerender.hbs │ └── weekly_prerender.hbs ├── wrangler.toml └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | }, 6 | "plugins": [ 7 | "react", 8 | ], 9 | "globals": { 10 | "graphql": false, 11 | }, 12 | "parserOptions": { 13 | "sourceType": "module", 14 | "ecmaFeatures": { 15 | "experimentalObjectRestSpread": true, 16 | "jsx": true, 17 | }, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | on: 2 | - push 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | name: Build 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: "[var] Yarn cache directory" 11 | id: yarn-cache-dir 12 | run: echo "::set-output name=dir::$(yarn cache dir)" 13 | - name: "[cache] .cache" 14 | uses: actions/cache@master 15 | with: 16 | key: gatsby-cache-folder 17 | path: .cache 18 | - name: "[cache] public" 19 | uses: actions/cache@master 20 | with: 21 | key: gatsby-public-folder 22 | path: public 23 | - name: "[cache] workers-site/dist/worker.js" 24 | uses: actions/cache@master 25 | with: 26 | key: ${{ runner.os }}-workers-site-dist-${{ hashFiles('**/worker.js') }} 27 | path: workers-site/dist/worker.js 28 | - name: "[cache] yarn cache" 29 | uses: actions/cache@master 30 | with: 31 | path: ${{ steps.yarn-cache-dir.outputs.dir }} 32 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 33 | restore-keys: | 34 | ${{ runner.os }}-yarn- 35 | - name: "[cache] node_modules" 36 | uses: actions/cache@master 37 | with: 38 | path: node_modules 39 | key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }} 40 | restore-keys: | 41 | ${{ runner.os }}-node_modules- 42 | - name: "[cache] workers-site node_modules" 43 | uses: actions/cache@master 44 | with: 45 | path: workers-site/node_modules 46 | key: ${{ runner.os }}-workkers-site-node_modules-${{ hashFiles('**/workers-site/package-lock.json') }} 47 | restore-keys: | 48 | ${{ runner.os }}-workers-site-node_modules- 49 | - run: 'yarn install --frozen-lockfile' 50 | - run: 'yarn run build' 51 | env: 52 | SANITY_READ_TOKEN: ${{ secrets.sanityReadToken }} 53 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # on: 2 | # push: 3 | # branches: 4 | # - master 5 | # repository_dispatch: 6 | # schedule: 7 | # - cron: '*/10 * * * *' 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | name: Deploy 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: "[var] Yarn cache directory" 16 | id: yarn-cache-dir 17 | run: echo "::set-output name=dir::$(yarn cache dir)" 18 | - name: "[cache] .cache" 19 | uses: actions/cache@master 20 | with: 21 | key: gatsby-cache-folder 22 | path: .cache 23 | - name: "[cache] public" 24 | uses: actions/cache@master 25 | with: 26 | key: gatsby-public-folder 27 | path: public 28 | - name: "[cache] workers-site/dist/worker.js" 29 | uses: actions/cache@master 30 | with: 31 | key: ${{ runner.os }}-workers-site-dist-${{ hashFiles('**/worker.js') }} 32 | path: workers-site/dist/worker.js 33 | - name: "[cache] yarn cache" 34 | uses: actions/cache@master 35 | with: 36 | path: ${{ steps.yarn-cache-dir.outputs.dir }} 37 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 38 | restore-keys: | 39 | ${{ runner.os }}-yarn- 40 | - name: "[cache] node_modules" 41 | uses: actions/cache@master 42 | with: 43 | path: node_modules 44 | key: ${{ runner.os }}-node_modules-${{ hashFiles('**/yarn.lock') }} 45 | restore-keys: | 46 | ${{ runner.os }}-node_modules- 47 | - name: "[cache] workers-site node_modules" 48 | uses: actions/cache@master 49 | with: 50 | path: workers-site/node_modules 51 | key: ${{ runner.os }}-workkers-site-node_modules-${{ hashFiles('**/workers-site/package-lock.json') }} 52 | restore-keys: | 53 | ${{ runner.os }}-workers-site-node_modules- 54 | - run: 'yarn install --frozen-lockfile' 55 | - run: 'yarn run build' 56 | env: 57 | SANITY_READ_TOKEN: ${{ secrets.sanityReadToken }} 58 | - name: "[ci] Publish with Cloudflare Workers" 59 | uses: cloudflare/wrangler-action@1.2.0 60 | with: 61 | apiToken: ${{ secrets.apiToken }} 62 | environment: 'production' 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # dotenv environment variables file 55 | .env* 56 | 57 | # gatsby files 58 | .cache/ 59 | public 60 | 61 | # Mac files 62 | .DS_Store 63 | 64 | # Yarn 65 | yarn-error.log 66 | .pnp/ 67 | .pnp.js 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | dist 72 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "semi": false, 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | os: 4 | - linux 5 | - osx 6 | 7 | node_js: 8 | - "node" 9 | - "lts/*" 10 | - "7" 11 | - "8" 12 | 13 | script: 14 | - npm install 15 | - npm run lint 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Gatsbyjs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bytesized 2 | 3 | 👋 This is the source code for [bytesized.xyz](https://www.bytesized.xyz)! 4 | 5 | Bytesized Code is [my](https://www.bytesized.xyz) project bringing you screencasts, tutorials, blog posts, and Byteconf -- a free developer conference series, streamed online (like [Byteconf React](https://www.bytesized.xyz/react-2018))! 6 | 7 | This website is the landing page for all the Byteconf events, and an increasing number of additional things like [blog posts](https://www.bytesized.xyz), newsletter landing pages, etc. 8 | 9 | The tech stack: 10 | 11 | - The site is built with [Gatsby](https://gatsbyjs.org) 12 | - Event, speaker, and talk data is sourced from our [Sanity.io](https://sanity.io) instance. Our Sanity.io schema is open-source, [check it out](https://github.com/byteconf/byteconf-sanity-schema)! 13 | - Blog posts are sourced from our [Ghost](https://ghost.org) instance. [Interested in contributing?](https://www.bytesized.xyz/accepting-story-submissions) 14 | - The site is [deployed hourly using GitHub Actions](https://github.com/signalnerve/bytesized/blob/master/.github/workflows/deploy.yml), and hosted with [Cloudflare Workers](https://workers.cloudflare.com). 15 | 16 | We're interested in contributions, but we're still trying to figure out how to do that well! More to come soon 👀 17 | 18 | ## Running in development 19 | 20 | `gatsby develop` 21 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config({ 2 | path: `.env.${process.env.NODE_ENV}`, 3 | }) 4 | 5 | const fs = require('fs') 6 | const { buildClientSchema } = require('graphql') 7 | 8 | module.exports = { 9 | siteMetadata: { 10 | title: 'Bytesized', 11 | author: 'Bytesized', 12 | description: 'A developer community for everyone', 13 | siteUrl: 'https://www.bytesized.xyz', 14 | }, 15 | plugins: [ 16 | { 17 | resolve: `gatsby-plugin-google-analytics`, 18 | options: { 19 | trackingId: `UA-107917910-6`, 20 | }, 21 | }, 22 | { 23 | resolve: `gatsby-plugin-mdx`, 24 | options: { 25 | defaultLayouts: { 26 | default: require.resolve('./src/components/MdxLayout.js'), 27 | }, 28 | }, 29 | }, 30 | `gatsby-plugin-postcss`, 31 | `gatsby-plugin-sass`, 32 | { 33 | resolve: `gatsby-source-ghost`, 34 | options: { 35 | apiUrl: `https://blog.bytesized.xyz`, 36 | contentApiKey: `04c5f19f6ffede798a15d61f7e`, 37 | }, 38 | }, 39 | { 40 | resolve: 'gatsby-source-sanity', 41 | options: { 42 | projectId: '82qqyrei', 43 | dataset: 'byteconf', 44 | watchMode: process.env.NODE_ENV == 'development', 45 | overlayDrafts: process.env.NODE_ENV == 'development', 46 | token: process.env.SANITY_READ_TOKEN, 47 | }, 48 | }, 49 | { 50 | resolve: `gatsby-source-filesystem`, 51 | options: { 52 | path: `${__dirname}/src/assets`, 53 | name: 'assets', 54 | }, 55 | }, 56 | { 57 | resolve: `gatsby-source-filesystem`, 58 | options: { 59 | path: `${__dirname}/src/pages`, 60 | name: 'pages', 61 | }, 62 | }, 63 | { 64 | resolve: `gatsby-transformer-remark`, 65 | options: { 66 | plugins: [ 67 | { 68 | resolve: `gatsby-remark-images`, 69 | options: { 70 | maxWidth: 590, 71 | }, 72 | }, 73 | { 74 | resolve: `gatsby-remark-responsive-iframe`, 75 | options: { 76 | wrapperStyle: `margin-bottom: 1.0725rem`, 77 | }, 78 | }, 79 | 'gatsby-remark-prismjs', 80 | 'gatsby-remark-copy-linked-files', 81 | 'gatsby-remark-smartypants', 82 | ], 83 | }, 84 | }, 85 | `gatsby-plugin-sharp`, 86 | `gatsby-transformer-sharp`, 87 | { 88 | resolve: `gatsby-plugin-manifest`, 89 | options: { 90 | name: `Bytesized Code`, 91 | short_name: `Bytesized Code`, 92 | start_url: `/`, 93 | background_color: `#ffffff`, 94 | theme_color: `#663399`, 95 | display: `minimal-ui`, 96 | icon: `src/assets/byteconf-full.png`, 97 | }, 98 | }, 99 | `gatsby-plugin-offline`, 100 | `gatsby-plugin-react-helmet`, 101 | ], 102 | } 103 | -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const { createFilePath } = require('gatsby-source-filesystem') 3 | 4 | exports.onCreateNode = ({ node, actions, getNode }) => { 5 | const { createNodeField } = actions 6 | 7 | if (node.internal.type === `MarkdownRemark`) { 8 | const value = createFilePath({ node, getNode }) 9 | createNodeField({ 10 | name: `slug`, 11 | node, 12 | value, 13 | }) 14 | } 15 | } 16 | 17 | exports.createPages = ({ graphql, actions }) => { 18 | const { createPage, createRedirect } = actions 19 | 20 | const routes = [ 21 | { 22 | fromPath: '/s/youtube', 23 | toPath: 'https://www.youtube.com/channel/UC046lFvJZhiwSRWsoH8SFjg', 24 | }, 25 | { 26 | fromPath: '/s/instagram', 27 | toPath: 'https://instagram.com/bytesizedcode', 28 | }, 29 | { 30 | fromPath: '/s/medium', 31 | toPath: 'https://medium.com/byteconf', 32 | }, 33 | { 34 | fromPath: '/s/github', 35 | toPath: 'https://github.com/signalnerve/bytesized', 36 | }, 37 | { 38 | fromPath: '/s/patreon', 39 | toPath: 'https://www.patreon.com/bytesizedcode/', 40 | }, 41 | { 42 | fromPath: '/patrons', 43 | toPath: 'https://www.patreon.com/bytesizedcode/', 44 | }, 45 | { 46 | fromPath: '/s/twitch', 47 | toPath: 'https://twitch.tv/byteconf', 48 | }, 49 | { 50 | fromPath: '/discord', 51 | toPath: 'https://discordapp.com/invite/dTKdswv', 52 | }, 53 | { 54 | fromPath: '/s/discord', 55 | toPath: 'https://discordapp.com/invite/dTKdswv', 56 | }, 57 | { 58 | fromPath: '/s/newsletter', 59 | toPath: 'https://www.bytesized.xyz/newsletter', 60 | }, 61 | { 62 | fromPath: '/s/blog', 63 | toPath: 'https://blog.byteconf.com', 64 | }, 65 | { 66 | fromPath: '/s/twitter', 67 | toPath: 'https://twitter.com/bytesizedcode', 68 | }, 69 | { 70 | fromPath: '/s/periscope', 71 | toPath: 'https://www.pscp.tv/byteconf', 72 | }, 73 | { 74 | fromPath: '/sponsor', 75 | toPath: 'https://bytesized.typeform.com/to/wBXCdI', 76 | }, 77 | { 78 | fromPath: '/sponsors', 79 | toPath: 'https://bytesized.typeform.com/to/wBXCdI', 80 | }, 81 | ] 82 | 83 | routes.forEach(({ fromPath, toPath, redirectInBrowser = true }) => { 84 | createRedirect({ 85 | fromPath, 86 | toPath, 87 | redirectInBrowser, 88 | }) 89 | }) 90 | 91 | return new Promise((resolve, reject) => { 92 | const eventTemplate = path.resolve(`src/templates/event.js`) 93 | const eventRSVPTemplate = path.resolve(`src/templates/eventRSVP.js`) 94 | const postTemplate = path.resolve(`src/templates/blogPost.js`) 95 | const tagTemplate = path.resolve(`src/templates/tag.js`) 96 | 97 | // Query for markdown nodes to use in creating pages. 98 | resolve( 99 | graphql( 100 | ` 101 | { 102 | allSanityEvent { 103 | edges { 104 | node { 105 | slug 106 | } 107 | } 108 | } 109 | allGhostPost { 110 | edges { 111 | node { 112 | slug 113 | } 114 | } 115 | } 116 | allGhostTag { 117 | edges { 118 | node { 119 | name 120 | slug 121 | } 122 | } 123 | } 124 | } 125 | ` 126 | ).then(result => { 127 | if (result.errors) { 128 | reject(result.errors) 129 | } 130 | 131 | result.data.allGhostPost.edges.forEach(({ node: { slug } }) => { 132 | createPage({ 133 | path: `/${slug}`, 134 | component: postTemplate, 135 | context: { 136 | slug, 137 | }, 138 | }) 139 | }) 140 | 141 | result.data.allGhostTag.edges.forEach(({ node: { name, slug } }) => { 142 | createPage({ 143 | path: `/${slug}`, 144 | component: tagTemplate, 145 | context: { 146 | name, 147 | slug, 148 | }, 149 | }) 150 | }) 151 | 152 | // Create pages for each markdown file. 153 | result.data.allSanityEvent.edges.forEach(({ node: { slug } }) => { 154 | createPage({ 155 | path: slug, 156 | component: eventTemplate, 157 | context: { 158 | slug, 159 | }, 160 | }) 161 | createPage({ 162 | path: slug + '/thanks', 163 | component: eventRSVPTemplate, 164 | context: { 165 | slug, 166 | }, 167 | }) 168 | }) 169 | }) 170 | ) 171 | }) 172 | } 173 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bytesized", 3 | "description": "website for bytesized.xyz", 4 | "version": "1.0.0", 5 | "author": "Kristian Freeman ", 6 | "bugs": { 7 | "url": "https://github.com/gatsbyjs/gatsby-starter-blog/issues" 8 | }, 9 | "dependencies": { 10 | "@cloudflare/wrangler": "latest", 11 | "@mdx-js/mdx": "^1.5.1", 12 | "@mdx-js/react": "^1.5.1", 13 | "classnames": "^2.2.6", 14 | "gatsby": "latest", 15 | "gatsby-image": "^2.2.36", 16 | "gatsby-plugin-feed": "^2.0.8", 17 | "gatsby-plugin-google-analytics": "^2.0.5", 18 | "gatsby-plugin-manifest": "^2.0.5", 19 | "gatsby-plugin-mdx": "^1.0.56", 20 | "gatsby-plugin-offline": "^2.0.5", 21 | "gatsby-plugin-postcss": "^2.1.10", 22 | "gatsby-plugin-react-helmet": "^3.0.0", 23 | "gatsby-plugin-sass": "^2.0.7", 24 | "gatsby-plugin-sharp": "^2.0.6", 25 | "gatsby-plugin-typography": "^2.2.0", 26 | "gatsby-remark-copy-linked-files": "^2.0.5", 27 | "gatsby-remark-images": "^2.0.4", 28 | "gatsby-remark-prismjs": "^3.0.0", 29 | "gatsby-remark-responsive-iframe": "^2.0.5", 30 | "gatsby-remark-smartypants": "^2.0.5", 31 | "gatsby-source-filesystem": "^2.0.2", 32 | "gatsby-source-ghost": "^4.0.1", 33 | "gatsby-source-graphql": "^2.1.17", 34 | "gatsby-source-sanity": "^5.0.4", 35 | "gatsby-transformer-remark": "^2.1.6", 36 | "gatsby-transformer-sharp": "^2.2.23", 37 | "handlebars": "^4.7.3", 38 | "handlebars-loader": "^1.7.1", 39 | "isbot": "^2.5.7", 40 | "lodash": "^4.17.11", 41 | "lscache": "^1.3.0", 42 | "luxon": "^1.8.2", 43 | "moment": "^2.23.0", 44 | "node-sass": "^4.10.0", 45 | "path-to-regexp": "^6.1.0", 46 | "prismjs": "^1.15.0", 47 | "react": "^16.9.0", 48 | "react-dom": "^16.9.0", 49 | "react-helmet": "^5.2.0", 50 | "react-lazyload": "^2.3.0", 51 | "react-markdown": "^4.0.4", 52 | "react-player": "^1.9.3", 53 | "react-share": "^3.0.1", 54 | "react-twitter-embed": "^3.0.3", 55 | "tailwindcss-transitions": "^2.1.0" 56 | }, 57 | "devDependencies": { 58 | "eslint": "^4.19.1", 59 | "eslint-plugin-react": "^7.11.1", 60 | "prettier": "^1.14.2", 61 | "tailwindcss": "^1.2.0" 62 | }, 63 | "homepage": "https://github.com/gatsbyjs/gatsby-starter-blog#readme", 64 | "keywords": [ 65 | "gatsby" 66 | ], 67 | "license": "MIT", 68 | "main": "n/a", 69 | "repository": { 70 | "type": "git", 71 | "url": "git+https://github.com/gatsbyjs/gatsby-starter-blog.git" 72 | }, 73 | "scripts": { 74 | "gatsby:develop": "gatsby develop", 75 | "gatsby:build": "gatsby build", 76 | "gatsby:build:incremental": "GATSBY_EXPERIMENTAL_PAGE_BUILD_ON_DATA_CHANGES=true gatsby build --log-pages", 77 | "workers-site:build": "cd workers-site && npm run build", 78 | "build": "yarn run gatsby:build && npm run workers-site:build", 79 | "build:incremental": "yarn run gatsby:build:incremental && yarn run workers-site:build", 80 | "start": "yarn run develop", 81 | "deploy": "yarn run build && wrangler publish" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = () => ({ 2 | plugins: [require("tailwindcss")], 3 | }) 4 | -------------------------------------------------------------------------------- /site.webpack.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | target: 'webworker', 3 | entry: './workers-site/index.js', 4 | module: { 5 | rules: [{ test: /\.hbs$/, loader: 'handlebars-loader' }], 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/byteconf-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristianfreeman/bytesized/6055caf82a62aa463989d4f70d5e9bc89b6af6f5/src/assets/byteconf-full.png -------------------------------------------------------------------------------- /src/assets/byteconf-no-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristianfreeman/bytesized/6055caf82a62aa463989d4f70d5e9bc89b6af6f5/src/assets/byteconf-no-bg.png -------------------------------------------------------------------------------- /src/components/CTA/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristianfreeman/bytesized/6055caf82a62aa463989d4f70d5e9bc89b6af6f5/src/components/CTA/bg.jpg -------------------------------------------------------------------------------- /src/components/CTA/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import bg from './bg.jpg' 3 | 4 | const CTA = () => ( 5 |
13 |
14 |

15 | Join our weekly newsletter 16 |

17 |

18 | Every Tuesday you'll get Bytesized Weekly in your 19 | inbox, with the latest and greatest in the web development world. No 20 | spam, and you can unsubscribe at any time. 21 |

22 |

23 | 27 | Join now 28 | 29 |

30 |
31 |
32 | ) 33 | 34 | export default CTA 35 | -------------------------------------------------------------------------------- /src/components/Event.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby' 3 | 4 | const s3Url = path => `https://byteconf-production.s3.amazonaws.com/${path}` 5 | 6 | const Container = ({ children, image, recent, url }) => { 7 | const imageStyle = { 8 | backgroundImage: `url(${image ? image.asset.fluid.src : url})`, 9 | } 10 | return ( 11 |
21 | {children} 22 |
23 | ) 24 | } 25 | 26 | const Event = ({ event, recent }) => { 27 | return ( 28 | 37 | 42 |
43 |
44 |

49 | {event.name} 50 |

51 |
52 |
53 |
54 | 55 | ) 56 | } 57 | 58 | export default Event 59 | -------------------------------------------------------------------------------- /src/components/Event/Header.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | class EventHeader extends React.Component { 4 | render() { 5 | const { event } = this.props 6 | 7 | return ( 8 |
9 |
10 |
11 |
12 | 16 | Byteconf 17 | 18 |
19 |
20 |
21 |
22 | ) 23 | } 24 | } 25 | 26 | export default EventHeader 27 | -------------------------------------------------------------------------------- /src/components/Event/Hero.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import Markdown from 'react-markdown' 4 | import illustration from './illustration.svg' 5 | import './illustration.css' 6 | import { DateTime } from 'luxon' 7 | import RelativeTime from '../RelativeTime' 8 | 9 | import System from '../../system' 10 | import Form from '../Form' 11 | 12 | const RSVP = ({ event: { name, ck_rsvp_form_id, status } }) => { 13 | const submitText = 14 | status === 'concluding' ? `Send me the swag bag` : `I'm going!` 15 | return ( 16 |
17 |

18 | {status === 'concluding' 19 | ? `Attended ${name}? Get the free conference swag bag in your inbox` 20 | : `RSVP for ${name}`} 21 |

22 |
23 |
24 | ) 25 | } 26 | 27 | const DateString = ({ start_date, end_date }) => { 28 | const start = DateTime.fromISO(start_date) 29 | const end = DateTime.fromISO(end_date) 30 | 31 | if (start.hasSame(end, 'day')) { 32 | return 33 | } else { 34 | return ( 35 | 36 | 37 | 38 | ) 39 | } 40 | } 41 | 42 | class EventHero extends React.Component { 43 | render() { 44 | const { event } = this.props 45 | 46 | return ( 47 |
48 |
49 |

{event.name}

50 |
51 |
52 |
53 |
54 | 58 |
59 | 60 |
61 | 65 | {event.simple_copy} 66 | 67 |
68 | 69 | {event.youtube_playlist && ( 70 | 84 | )} 85 |
86 | {event.status != 'finished' ? ( 87 | 88 | ) : ( 89 | {event.name} 94 | )} 95 |
96 |
97 | ) 98 | } 99 | } 100 | 101 | export default EventHero 102 | -------------------------------------------------------------------------------- /src/components/Event/Organizers.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Organizers = ({ event: { organizers } }) => ( 4 |
5 |
6 |

Organizers

7 | 8 |
9 |
10 | {!!organizers && 11 | organizers.map(({ image, name, twitter, url }) => ( 12 |
13 |
14 | 19 |
20 | 21 |
22 |
{name}
23 |
24 | {twitter && ( 25 | 30 | 31 | 32 | )} 33 | {url && ( 34 | 35 | 36 | 37 | )} 38 |
39 |
40 |
41 | ))} 42 |
43 |
44 |
45 |
46 | ) 47 | 48 | export default Organizers 49 | -------------------------------------------------------------------------------- /src/components/Event/Schedule.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import { DateTime } from 'luxon' 4 | import RelativeTime from '../RelativeTime' 5 | import Talk from '../Talk' 6 | import { groupBy, orderBy } from 'lodash' 7 | 8 | const Schedule = ({ event }) => { 9 | const groupedTalks = groupBy(event.talks, talk => 10 | DateTime.fromISO(talk.time) 11 | .setZone('UTC') 12 | .toISODate() 13 | ) 14 | 15 | const renderedTalks = ( 16 | <> 17 |
18 | {Object.keys(groupedTalks).map(key => { 19 | return ( 20 |
21 |

22 | 23 |

24 | {orderBy(groupedTalks[key], 'time').map(talk => ( 25 | 31 | ))} 32 |
33 | ) 34 | })} 35 |
36 | 37 | ) 38 | 39 | return ( 40 |
41 |

42 | Schedule 43 |

44 | {event.talks.length ? ( 45 | renderedTalks 46 | ) : ( 47 |

48 | Stay tuned for the conference schedule, coming soon! 49 |

50 | )} 51 |
52 | ) 53 | } 54 | 55 | export default Schedule 56 | -------------------------------------------------------------------------------- /src/components/Event/Speakers.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import Speaker from '../Speaker' 4 | 5 | const EventSpeakers = ({ event }) => ( 6 | <> 7 |

8 | Speakers 9 |

10 | 11 |
12 |
13 | {event.event_speakers.map(({ speaker }) => ( 14 | 15 | ))} 16 |
17 |
18 | 19 | ) 20 | 21 | export default EventSpeakers 22 | -------------------------------------------------------------------------------- /src/components/Event/Sponsors.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import S3Url from '../../utils/s3Url' 4 | 5 | const Sponsors = () => ( 6 |
7 |

Sponsors

8 | 9 |
10 |
11 |
12 |
13 | 14 | 15 | 16 |
17 |
18 |
19 |
20 | 21 |

Community Partners

22 | 23 |
24 |
25 |
26 |
27 | 28 | 29 | 30 |
31 |
32 |
33 |
34 |
35 | ) 36 | 37 | export default Sponsors 38 | -------------------------------------------------------------------------------- /src/components/Event/illustration.css: -------------------------------------------------------------------------------- 1 | @keyframes float { 2 | 0% { 3 | transform: translatey(0px); 4 | } 5 | 50% { 6 | transform: translatey(-40px); 7 | } 8 | 100% { 9 | transform: translatey(0px); 10 | } 11 | } 12 | 13 | .illustration { 14 | transform: translatey(0px); 15 | animation: float 10s ease-in-out; 16 | } 17 | -------------------------------------------------------------------------------- /src/components/Event/illustration.svg: -------------------------------------------------------------------------------- 1 | youtube tutorial -------------------------------------------------------------------------------- /src/components/Events.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { get, orderBy } from 'lodash' 3 | import { useStaticQuery, graphql } from 'gatsby' 4 | 5 | import moment from 'moment' 6 | 7 | import Event from './Event' 8 | 9 | const TODAY = new Date() 10 | 11 | const isToday = date => moment(date).isSame(TODAY, 'day') 12 | const afterToday = date => new Date(date) > TODAY 13 | 14 | const Events = () => { 15 | const data = useStaticQuery(graphql` 16 | query { 17 | allSanityEvent(filter: { published: { eq: true } }) { 18 | edges { 19 | node { 20 | _id 21 | description 22 | name 23 | slug 24 | start_date 25 | end_date 26 | youtube_playlist 27 | cover_path 28 | cover { 29 | asset { 30 | fluid(maxWidth: 800) { 31 | ...GatsbySanityImageFluid 32 | } 33 | } 34 | } 35 | event_type 36 | status 37 | } 38 | } 39 | } 40 | } 41 | `) 42 | 43 | const events = get(data, 'allSanityEvent.edges', []).map(e => e.node) 44 | const sorted = orderBy(events, 'start_date', 'desc') 45 | const filtered = sorted.filter(({ status }) => status !== 'planning') 46 | const [recent, ...announced] = filtered 47 | 48 | return ( 49 |
50 |

Events

51 |

52 | Byteconf is our free developer conference series, streamed online. Learn 53 | from the best speakers and teachers in the programming world, right from 54 | your home. 55 |

56 |
57 |
58 | 59 |
60 |
61 | {orderBy(announced, 'start_date', 'desc').map(event => ( 62 | 63 | ))} 64 |
65 |
66 |
67 | ) 68 | } 69 | 70 | export default Events 71 | -------------------------------------------------------------------------------- /src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import { Link } from 'gatsby' 4 | 5 | import logo from '../assets/byteconf-no-bg.png' 6 | import Social from './Social' 7 | 8 | const Footer = () => ( 9 |
10 |
11 |
12 | 13 | Bytesized 14 | 15 |
16 | 17 |
18 |
19 |
20 |

Bytesized Code

21 |
    22 |
  • 23 | 27 | Byteconf 28 | 29 |
  • 30 |
  • 31 | 32 | Videos 33 | 34 |
  • 35 |
  • 36 | 40 | JavaScript 41 | 42 |
  • 43 |
  • 44 | 45 | React 46 | 47 |
  • 48 |
  • 49 | 50 | Career 51 | 52 |
  • 53 |
  • 54 | 55 | Blog 56 | 57 |
  • 58 |
  • 59 | 60 | Newsletter 61 | 62 |
  • 63 |
64 |
65 |
66 |

67 | Byteconf attendees and speakers are expected to follow our{' '} 68 | 72 | Code of Conduct 73 | 74 | . 75 |

76 |
77 |
78 | 79 |
80 |

81 | © Copyright Bytesized, LLC 2018-2020. All Rights Reserved. 82 |

83 |
84 |
85 | ) 86 | 87 | export default Footer 88 | -------------------------------------------------------------------------------- /src/components/Form.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | export default ({ formId, submitText = 'Submit' }) => ( 3 | <> 4 | 5 | 13 | 21 | 28 | 35 | 36 | 37 | ) 38 | -------------------------------------------------------------------------------- /src/components/HomepageHero/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { first, get, orderBy } from 'lodash' 3 | import { Link, useStaticQuery, graphql } from 'gatsby' 4 | 5 | import S3Url from '../../utils/s3Url' 6 | 7 | const HomepageHero = () => { 8 | const data = useStaticQuery(graphql` 9 | query { 10 | allGhostPost(limit: 1, sort: { fields: [published_at], order: DESC }) { 11 | edges { 12 | node { 13 | slug 14 | title 15 | published_at 16 | feature_image 17 | } 18 | } 19 | } 20 | # Need to re-add published filter 21 | allSanityEvent { 22 | edges { 23 | node { 24 | name 25 | description 26 | slug 27 | start_date 28 | cover { 29 | asset { 30 | fluid(maxWidth: 1600) { 31 | ...GatsbySanityImageFluid 32 | } 33 | } 34 | } 35 | cover_path 36 | status 37 | } 38 | } 39 | } 40 | } 41 | `) 42 | 43 | const posts = get(data, 'allGhostPost.edges', []).map(p => p.node) 44 | const heroPost = first(orderBy(posts, 'published_at', 'desc')) 45 | 46 | const events = get(data, 'allSanityEvent.edges', []).map(e => e.node) 47 | const heroEvent = first(orderBy(events, 'start_date', 'desc')) 48 | 49 | const heroItem = ['announced', 'upcoming'].includes(heroEvent.status) 50 | ? { 51 | name: heroEvent.name, 52 | slug: heroEvent.slug, 53 | description: heroEvent.description, 54 | image: heroEvent.cover 55 | ? heroEvent.cover.asset.fluid.src 56 | : S3Url(heroEvent.cover_path || 'headers/attendees.jpg'), 57 | } 58 | : { 59 | name: heroPost.title, 60 | slug: heroPost.slug, 61 | description: heroPost.description, 62 | image: heroPost.feature_image, 63 | } 64 | 65 | return ( 66 | 67 |
76 |
77 | What's new 78 |
79 |
80 |

81 | {heroItem.name} 82 |

83 | {heroItem.description && ( 84 |

{heroItem.description}

85 | )} 86 |
87 |
88 | 89 | ) 90 | } 91 | 92 | export default HomepageHero 93 | -------------------------------------------------------------------------------- /src/components/Layout.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | /* Measure is limited to ~66 characters */ 6 | .measure { 7 | max-width: 27em; 8 | } 9 | 10 | /* Measure is limited to ~80 characters */ 11 | .measure-wide { 12 | max-width: 34em; 13 | } 14 | 15 | /* Measure is limited to ~45 characters */ 16 | .measure-narrow { 17 | max-width: 20em; 18 | } 19 | 20 | @variants hover { 21 | .lighten-sm { 22 | filter: brightness(110%); 23 | } 24 | 25 | .lighten { 26 | filter: brightness(120%); 27 | } 28 | 29 | .darken { 30 | filter: brightness(80%); 31 | } 32 | 33 | .grayscale-0 { 34 | filter: grayscale(0); 35 | } 36 | 37 | .grayscale-1 { 38 | filter: grayscale(1); 39 | } 40 | } 41 | 42 | @variants responsive { 43 | .h-vh-20 { 44 | height: 20vh; 45 | } 46 | 47 | .h-vh-40 { 48 | height: 40vh; 49 | } 50 | 51 | .h-vh-60 { 52 | height: 60vh; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/components/Layout.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Helmet from 'react-helmet' 3 | import { StaticQuery, graphql } from 'gatsby' 4 | 5 | import favicon from '../assets/byteconf-full.png' 6 | import './Layout.css' 7 | 8 | import CTA from './CTA' 9 | import Footer from './Footer' 10 | import Nav from './Nav' 11 | import Share from './Share' 12 | 13 | const Layout = ({ children, title }) => { 14 | return ( 15 | { 18 | const siteTitle = data.site.siteMetadata.title 19 | const siteDescription = data.site.siteMetadata.description 20 | return ( 21 |
22 | 29 | 30 | 36 | 40 | 41 | 45 | 49 | 53 | 54 | 58 | 59 | 60 | 61 | 62 |