├── .gitignore ├── LICENSE ├── README.md ├── demo ├── content │ └── episodes │ │ ├── 1 │ │ ├── guest.png │ │ └── index.md │ │ ├── 2 │ │ ├── banner.png │ │ ├── guest.png │ │ └── index.md │ │ ├── 3 │ │ ├── banner.png │ │ ├── guest.png │ │ └── index.md │ │ ├── 4 │ │ ├── banner.png │ │ └── index.md │ │ ├── 5 │ │ ├── banner.png │ │ └── index.md │ │ ├── 6 │ │ ├── banner.png │ │ └── index.md │ │ ├── 7 │ │ ├── banner.png │ │ └── index.md │ │ └── 8 │ │ ├── banner.png │ │ └── index.md ├── gatsby-config.js ├── package.json └── src │ ├── @vojtaholik │ └── gatsby-theme-simplecast │ │ └── lib │ │ └── config │ │ └── index.js │ ├── gatsby-plugin-theme-ui │ └── index.js │ ├── images │ └── icon.png │ └── pages │ ├── 404.js │ ├── index.js │ └── page-2.js ├── gatsby-theme-simplecast ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── data │ └── mockupEpisodes.json ├── gatsby-browser.js ├── gatsby-config.js ├── gatsby-node.js ├── gatsby-ssr.js ├── index.js ├── package.json ├── src │ ├── components │ │ ├── aside.js │ │ ├── bars.js │ │ ├── context.js │ │ ├── header.js │ │ ├── image.js │ │ ├── layout.css │ │ ├── layout.js │ │ ├── link.js │ │ ├── navigation.js │ │ ├── player.js │ │ ├── seo.js │ │ └── volumeBars.js │ ├── gatsby-plugin-theme-ui │ │ └── index.js │ ├── images │ │ ├── apple.svg │ │ ├── google.svg │ │ ├── icon.png │ │ └── spotify.png │ ├── lib │ │ ├── config │ │ │ └── index.js │ │ └── formatTime.js │ ├── pages │ │ ├── 404.js │ │ └── index.js │ └── templates │ │ └── episode.js └── yarn.lock ├── package.json └── yarn.lock /.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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vojta Holik 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 | # Gatsby Theme Simplecast 2 | 3 | ### [→ Preview](https://gatsby-theme-simplecast.netlify.com) 4 | 5 | 6 | 7 | ## Running demo 8 | - `yarn` 9 | - `yarn workspace demo develop` 10 | 11 | ## Using theme 12 | - [Read me →](https://github.com/vojtaholik/gatsby-theme-simplecast/blob/master/gatsby-theme-simplecast/README.md) -------------------------------------------------------------------------------- /demo/content/episodes/1/guest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vojtaholik/gatsby-theme-simplecast/2a67a6834ccb431886e7c0bcc9c7c8b4c1df4dfe/demo/content/episodes/1/guest.png -------------------------------------------------------------------------------- /demo/content/episodes/1/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: a 3 | summary: 'Summary. Lorem ipsum dolor sit amet.' 4 | resources: ['[Link](/)','[Link](/)','[Link](/)'] 5 | guestName: 'Anna Doe' 6 | guestSummary: '[Twitter](/)' 7 | image: null 8 | guestPhoto: ./guest.png 9 | --- 10 | 11 | Ground round pork shoulder buffalo, short ribs chuck bresaola doner pig burgdoggen andouille turducken shankle strip steak fatback. Beef alcatra swine boudin corned beef drumstick hamburger tri-tip shankle. Meatball leberkas strip steak, burgdoggen biltong swine boudin. T-bone short ribs sausage tri-tip, rump pancetta bacon filet mignon jowl turducken ham. 12 | 13 | Tri-tip bacon cow rump. Andouille pig fatback kielbasa, venison bacon burgdoggen sirloin sausage tri-tip ground round frankfurter pork chop. Kielbasa shoulder hamburger salami drumstick pork tail landjaeger meatball burgdoggen ribeye pork belly cupim beef ribs. Biltong bacon bresaola, ribeye shank turducken pancetta beef ribs turkey t-bone. Biltong jerky buffalo venison ground round boudin. Picanha fatback t-bone, boudin capicola sirloin biltong. -------------------------------------------------------------------------------- /demo/content/episodes/2/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vojtaholik/gatsby-theme-simplecast/2a67a6834ccb431886e7c0bcc9c7c8b4c1df4dfe/demo/content/episodes/2/banner.png -------------------------------------------------------------------------------- /demo/content/episodes/2/guest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vojtaholik/gatsby-theme-simplecast/2a67a6834ccb431886e7c0bcc9c7c8b4c1df4dfe/demo/content/episodes/2/guest.png -------------------------------------------------------------------------------- /demo/content/episodes/2/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: b 3 | summary: 'Summary. Lorem ipsum dolor sit amet.' 4 | image: './banner.png' 5 | guestName: 'Charlie Doe' 6 | guestSummary: 'Lorem ipsum dolor sit amet. [Twitter](/)' 7 | guestPhoto: './guest.png' 8 | --- 9 | 10 | Maecenas a augue vitae mauris interdum accumsan. Morbi auctor velit sed justo tempor, nec blandit purus iaculis. In hac habitasse platea dictumst. Ut in rutrum justo. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam ullamcorper libero id sapien eleifend, et ornare lectus rhoncus. Donec tempor dapibus metus, quis ornare diam rhoncus eget. Ut molestie cursus ornare. Aenean iaculis eget enim congue vestibulum. Curabitur imperdiet nisi quis interdum auctor. 11 | 12 | Vivamus semper consectetur purus, at blandit nibh congue vel. Phasellus rhoncus, leo et commodo tempus, quam metus commodo tellus, sit amet interdum sem leo id nisl. Cras a pretium mauris. Sed nec justo ultricies, tincidunt lorem eu, accumsan dolor. Pellentesque eu auctor nisi, non tincidunt ante. Quisque lacinia dictum risus, a dignissim ipsum malesuada in. Morbi tristique risus metus, at porta mauris fermentum id. Quisque sit amet tincidunt enim, a auctor est. Morbi condimentum vestibulum ex non varius. -------------------------------------------------------------------------------- /demo/content/episodes/3/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vojtaholik/gatsby-theme-simplecast/2a67a6834ccb431886e7c0bcc9c7c8b4c1df4dfe/demo/content/episodes/3/banner.png -------------------------------------------------------------------------------- /demo/content/episodes/3/guest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vojtaholik/gatsby-theme-simplecast/2a67a6834ccb431886e7c0bcc9c7c8b4c1df4dfe/demo/content/episodes/3/guest.png -------------------------------------------------------------------------------- /demo/content/episodes/3/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: c 3 | summary: 'Summary. Lorem ipsum dolor sit amet.' 4 | resources: ['[Badass: Making Users Awesome](https://www.goodreads.com/book/show/24737268-badass)', 5 | '[Shambhala: The Sacred Path of the Warrior](https://www.goodreads.com/book/show/336248.Shambhala)'] 6 | guestName: 'Robert Doe' 7 | guestSummary: '[Twitter](https://twitter.com/janelleallen?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor)' 8 | guestPhoto: './guest.png' 9 | image: './banner.png' 10 | --- 11 | 12 | Flank pork belly leberkas pig jowl corned beef. Hamburger ham salami, venison pastrami strip steak spare ribs pork belly brisket. Bacon chicken tenderloin shankle tongue doner, corned beef shoulder burgdoggen cupim chuck pig turkey. Biltong brisket doner sirloin kielbasa short ribs porchetta prosciutto turkey leberkas ground round landjaeger spare ribs. Kevin strip steak tongue, bacon cupim chicken drumstick rump. 13 | 14 | Short ribs jowl biltong corned beef chuck, shoulder landjaeger burgdoggen frankfurter sausage hamburger meatball pork loin. Sausage doner beef ribs salami beef shankle. Jerky ground round rump, cow strip steak short loin doner ham hock meatloaf porchetta andouille beef ribs. Tri-tip ribeye doner sirloin, frankfurter meatball tail beef drumstick shankle. Kielbasa pancetta ham, flank fatback jowl turkey boudin leberkas. Tongue drumstick shankle, strip steak picanha pork filet mignon leberkas andouille flank landjaeger short loin corned beef meatball. -------------------------------------------------------------------------------- /demo/content/episodes/4/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vojtaholik/gatsby-theme-simplecast/2a67a6834ccb431886e7c0bcc9c7c8b4c1df4dfe/demo/content/episodes/4/banner.png -------------------------------------------------------------------------------- /demo/content/episodes/4/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: d 3 | summary: 'Summary. Lorem ipsum dolor sit amet.' 4 | image: './banner.png' 5 | resources: ['[Link](/)', '[Link](/)', '[Link](/)'] 6 | --- 7 | 8 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vel volutpat nulla, vitae egestas dui. Donec sodales eros id purus gravida, quis egestas lorem mollis. Morbi sapien velit, finibus eget dapibus eget, auctor a justo. Etiam cursus congue luctus. Nam dui dolor, convallis vel hendrerit fringilla, interdum vitae odio. Vivamus at interdum nunc, sed cursus magna. Praesent malesuada hendrerit magna nec euismod. Curabitur placerat pellentesque tincidunt. 9 | 10 | Donec odio justo, ornare ac massa vel, ullamcorper aliquam leo. Fusce facilisis diam vitae mi consequat suscipit. Nunc pharetra magna sit amet dui rhoncus malesuada. Integer hendrerit pellentesque lorem a pulvinar. Aenean at nunc tempus, semper nisi vitae, imperdiet nibh. Donec accumsan lacus ac sapien egestas aliquet. Sed lectus ex, auctor et varius quis, accumsan sed felis. Duis sit amet mattis nisi, sed tempor libero. Curabitur in tristique justo. Cras eu ipsum at nibh tristique iaculis. Suspendisse rhoncus laoreet fringilla. Cras tincidunt odio non facilisis fermentum. Quisque vel odio vel purus molestie pellentesque nec sit amet augue. Duis a neque sodales est pretium porttitor. Praesent at lacus rutrum, elementum dui ac, consequat mi. -------------------------------------------------------------------------------- /demo/content/episodes/5/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vojtaholik/gatsby-theme-simplecast/2a67a6834ccb431886e7c0bcc9c7c8b4c1df4dfe/demo/content/episodes/5/banner.png -------------------------------------------------------------------------------- /demo/content/episodes/5/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: e 3 | summary: 'Summary. Lorem ipsum dolor sit amet.' 4 | image: './banner.png' 5 | --- 6 | 7 | Pancetta ham hock tongue t-bone pork loin. Boudin burgdoggen alcatra, buffalo beef meatball ham corned beef short loin flank pancetta. Pastrami short loin spare ribs, alcatra shoulder rump pork kielbasa fatback meatball doner swine ribeye. Leberkas tongue shank ball tip prosciutto flank turkey frankfurter pig hamburger. Corned beef swine short ribs boudin. Drumstick rump salami shank jerky flank meatloaf strip steak bresaola bacon brisket tail meatball t-bone pig. Capicola boudin salami porchetta beef turkey brisket. 8 | 9 | Boudin buffalo chuck capicola. Short ribs meatloaf pork chop drumstick short loin pig frankfurter ground round meatball buffalo shoulder swine prosciutto tail ham. Venison meatball drumstick capicola, pig ribeye biltong fatback porchetta picanha sirloin. Ground round jowl porchetta shankle chuck pancetta. Kevin hamburger corned beef filet mignon tongue prosciutto beef ribs pancetta drumstick short loin chuck capicola. Shankle sausage porchetta shank. Sirloin short loin pork, ground round turkey ribeye chuck tail tri-tip t-bone buffalo bresaola. -------------------------------------------------------------------------------- /demo/content/episodes/6/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vojtaholik/gatsby-theme-simplecast/2a67a6834ccb431886e7c0bcc9c7c8b4c1df4dfe/demo/content/episodes/6/banner.png -------------------------------------------------------------------------------- /demo/content/episodes/6/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: f 3 | summary: 'Summary. Lorem ipsum dolor sit amet.' 4 | image: './banner.png' 5 | --- 6 | 7 | Ham salami biltong pastrami turkey frankfurter. Ham hock prosciutto pork, tenderloin beef ribs kielbasa swine biltong pancetta boudin turkey hamburger tongue strip steak jowl. Bresaola pork loin venison boudin. Shank pig porchetta boudin. Swine prosciutto short ribs kielbasa shankle ball tip flank ribeye biltong meatloaf salami bacon. 8 | 9 | Brisket burgdoggen shank corned beef, chicken frankfurter boudin pork loin shankle porchetta jowl ground round sirloin alcatra biltong. Turkey drumstick porchetta alcatra pork jerky, turducken biltong fatback cupim tail. Jerky filet mignon capicola, leberkas jowl brisket venison. Rump jerky boudin prosciutto, sausage short loin chicken pastrami buffalo. -------------------------------------------------------------------------------- /demo/content/episodes/7/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vojtaholik/gatsby-theme-simplecast/2a67a6834ccb431886e7c0bcc9c7c8b4c1df4dfe/demo/content/episodes/7/banner.png -------------------------------------------------------------------------------- /demo/content/episodes/7/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: g 3 | summary: 'Summary. Lorem ipsum dolor sit amet.' 4 | image: './banner.png' 5 | --- 6 | 7 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vel volutpat nulla, vitae egestas dui. Donec sodales eros id purus gravida, quis egestas lorem mollis. Morbi sapien velit, finibus eget dapibus eget, auctor a justo. Etiam cursus congue luctus. Nam dui dolor, convallis vel hendrerit fringilla, interdum vitae odio. Vivamus at interdum nunc, sed cursus magna. Praesent malesuada hendrerit magna nec euismod. Curabitur placerat pellentesque tincidunt. 8 | 9 | Donec odio justo, ornare ac massa vel, ullamcorper aliquam leo. Fusce facilisis diam vitae mi consequat suscipit. Nunc pharetra magna sit amet dui rhoncus malesuada. Integer hendrerit pellentesque lorem a pulvinar. Aenean at nunc tempus, semper nisi vitae, imperdiet nibh. Donec accumsan lacus ac sapien egestas aliquet. Sed lectus ex, auctor et varius quis, accumsan sed felis. Duis sit amet mattis nisi, sed tempor libero. Curabitur in tristique justo. Cras eu ipsum at nibh tristique iaculis. Suspendisse rhoncus laoreet fringilla. Cras tincidunt odio non facilisis fermentum. Quisque vel odio vel purus molestie pellentesque nec sit amet augue. Duis a neque sodales est pretium porttitor. Praesent at lacus rutrum, elementum dui ac, consequat mi. -------------------------------------------------------------------------------- /demo/content/episodes/8/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vojtaholik/gatsby-theme-simplecast/2a67a6834ccb431886e7c0bcc9c7c8b4c1df4dfe/demo/content/episodes/8/banner.png -------------------------------------------------------------------------------- /demo/content/episodes/8/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: h 3 | summary: 'Summary. Lorem ipsum dolor sit amet.' 4 | image: './banner.png' 5 | --- 6 | 7 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vel volutpat nulla, vitae egestas dui. Donec sodales eros id purus gravida, quis egestas lorem mollis. Morbi sapien velit, finibus eget dapibus eget, auctor a justo. Etiam cursus congue luctus. Nam dui dolor, convallis vel hendrerit fringilla, interdum vitae odio. Vivamus at interdum nunc, sed cursus magna. Praesent malesuada hendrerit magna nec euismod. Curabitur placerat pellentesque tincidunt. 8 | 9 | Donec odio justo, ornare ac massa vel, ullamcorper aliquam leo. Fusce facilisis diam vitae mi consequat suscipit. Nunc pharetra magna sit amet dui rhoncus malesuada. Integer hendrerit pellentesque lorem a pulvinar. Aenean at nunc tempus, semper nisi vitae, imperdiet nibh. Donec accumsan lacus ac sapien egestas aliquet. Sed lectus ex, auctor et varius quis, accumsan sed felis. Duis sit amet mattis nisi, sed tempor libero. Curabitur in tristique justo. Cras eu ipsum at nibh tristique iaculis. Suspendisse rhoncus laoreet fringilla. Cras tincidunt odio non facilisis fermentum. Quisque vel odio vel purus molestie pellentesque nec sit amet augue. Duis a neque sodales est pretium porttitor. Praesent at lacus rutrum, elementum dui ac, consequat mi. -------------------------------------------------------------------------------- /demo/gatsby-config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config({ 2 | path: `.env.${process.env.NODE_ENV}`, 3 | }) 4 | 5 | module.exports = { 6 | plugins: [ 7 | { 8 | resolve: '@vojtaholik/gatsby-theme-simplecast', 9 | options: { 10 | simplecastApiSecret: process.env.SIMPLECAST_API_SECRET, 11 | //podcastId: process.env.PODCAST_ID, 12 | markdownPath: 'content/episodes', 13 | episodeSlug: 'show', 14 | }, 15 | }, 16 | `gatsby-plugin-theme-ui`, 17 | ], 18 | } 19 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "demo", 4 | "version": "1.0.0", 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "gatsby build", 8 | "develop": "gatsby develop", 9 | "clean": "gatsby clean" 10 | }, 11 | "dependencies": { 12 | "@vojtaholik/gatsby-theme-simplecast": "^1.0.8", 13 | "gatsby": "^2.13.41", 14 | "gatsby-plugin-theme-ui": "^0.2.18", 15 | "gatsby-theme-ui": "^0.2.0", 16 | "react": "^16.8.6", 17 | "react-dom": "^16.8.6", 18 | "theme-ui": "^0.2.21" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /demo/src/@vojtaholik/gatsby-theme-simplecast/lib/config/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | podcastSeason: '1', 3 | headerImageHeight: [300, 400], 4 | spotifyUrl: '/', 5 | applePodcastsUrl: '/', 6 | googlePodcastsUrl: '/', 7 | } 8 | -------------------------------------------------------------------------------- /demo/src/gatsby-plugin-theme-ui/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | useCustomProperties: true, 3 | initialColorMode: 'dark', 4 | colors: { 5 | text: 'rgba(255, 255, 255, 0.9)', 6 | background: '#1A2232', 7 | backgroundLighten10: '#232B3B', 8 | backgroundLighten20: '#2C3648', 9 | primaryDarken: '#7A5EFF', 10 | primary: '#A085FF', 11 | primaryLighten10: '#9D82FF', 12 | primaryLighten50: '#B298FF', 13 | primaryLighten70: '#D2C8FF', 14 | secondary: '#85FFD0', 15 | }, 16 | } 17 | -------------------------------------------------------------------------------- /demo/src/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vojtaholik/gatsby-theme-simplecast/2a67a6834ccb431886e7c0bcc9c7c8b4c1df4dfe/demo/src/images/icon.png -------------------------------------------------------------------------------- /demo/src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function NotFound() { 4 | return ( 5 |
6 |

404 - page not found.

7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /demo/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {graphql} from 'gatsby' 3 | import IndexPage from '@vojtaholik/gatsby-theme-simplecast/src/pages/index' 4 | 5 | export default function Index({data: {allEpisode, allMarkdownRemark}}) { 6 | return 7 | } 8 | 9 | export const indexQuery = graphql` 10 | query { 11 | allEpisode { 12 | totalCount 13 | nodes { 14 | id 15 | title 16 | description 17 | number 18 | enclosure_url 19 | fields { 20 | slug 21 | } 22 | } 23 | } 24 | allMarkdownRemark { 25 | edges { 26 | node { 27 | html 28 | frontmatter { 29 | id 30 | title 31 | resources 32 | guestSummary 33 | guestName 34 | guestPhoto { 35 | childImageSharp { 36 | fluid(maxWidth: 200) { 37 | ...GatsbyImageSharpFluid_noBase64 38 | } 39 | } 40 | } 41 | image { 42 | childImageSharp { 43 | original { 44 | src 45 | } 46 | fluid(maxWidth: 700) { 47 | ...GatsbyImageSharpFluid_noBase64 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | } 55 | } 56 | ` 57 | -------------------------------------------------------------------------------- /demo/src/pages/page-2.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Page() { 4 | return ( 5 |
6 |

hello!

7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/.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.development 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 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": false, 4 | "singleQuote": false, 5 | "tabWidth": 2, 6 | "trailingComma": "es5" 7 | } 8 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/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 | 23 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/README.md: -------------------------------------------------------------------------------- 1 | # Gatsby Theme Simplecast 2 | 3 | Gatsby theme that sources data from Simplecast API which can be combined with Markdown files to associate more information to each episode. Inspired by [syntax.fm](http://syntax.fm). 4 | 5 | [→ Preview Theme](https://gatsby-theme-simplecast.netlify.com) 6 | 7 | ## Installation 8 | To use this theme in your Gatsby sites, follow these instructions: 9 | 10 | 1. Install the theme 11 | ``` 12 | yarn add @vojtaholik/gatsby-theme-simplecast 13 | ``` 14 | 2. Add the theme to your `gatsby-config.js`: 15 | ``` 16 | module.exports = { 17 | plugins: [ 18 | { 19 | resolve: '@vojtaholik/gatsby-theme-simplecast', 20 | options: { 21 | podcastId: PODCAST_ID, // theme uses mockup data if no podcastId provided 22 | simplecastApiSecret: SIMPLECAST_API_SECRET, 23 | markdownPath: 'content/episodes', 24 | }, 25 | }, 26 | ], 27 | } 28 | ``` 29 | Plugin options 30 | - `simplecastApiSecret`: Grab [your Simplecast API token here](https://dashboard.simplecast.com/account/private-apps). 31 | - `podcastId`: Podcast ID can be found in your Simplecast account under embeds settings. 32 | - `markdownPath`: Path to your markdown files. For markdown file to show up, it's `frontmatter.id` must match `episode.id`. 33 | - `episodeSlug`: default "show". (`/show/05/episode-title`) 34 | 35 | 3. Create index page in `src/pages/index.js` 36 | - You can use [this example](https://github.com/vojtaholik/gatsby-theme-simplecast/blob/master/demo/src/pages/index.js) which displays latest episode by default. 37 | 38 | 4. Start your site 39 | ``` 40 | gatsby develop 41 | ``` 42 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/data/mockupEpisodes.json: -------------------------------------------------------------------------------- 1 | { 2 | "href": "https://api.simplecast.com/podcasts/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/episodes?limit=10", 3 | "pages": { 4 | "total": 5, 5 | "previous": null, 6 | "next": { 7 | "href": "https://api.simplecast.com/podcasts/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/episodes?limit=10&offset=10&private=true&status=importing%2Caudio_imported%2Ctranscoding%2Ctranscoding_error%2Cdraft%2Cscheduled%2Cpublished%2Cdirty%2Cprivate" 8 | }, 9 | "limit": 10, 10 | "current": 1 11 | }, 12 | "dashboard_link": "https://dashboard.simplecast.com/shows/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/episodes", 13 | "create": null, 14 | "count": 47, 15 | "collection": [ 16 | { 17 | "updated_at": "2019-07-07T21:18:37.988516-07:00", 18 | "type": "full", 19 | "token": "GRGoBLV0", 20 | "title": "Podcast Episode Title", 21 | "status": "published", 22 | "slug": "podcast-episode-1", 23 | "season": { 24 | "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77", 25 | "number": 1 26 | }, 27 | "scheduled_for": null, 28 | "published_at": "2019-07-07T21:18:37.988429-07:00", 29 | "number": 8, 30 | "is_hidden": false, 31 | "image_url": "https://cdn.simplecast.com/images/55d7f360-05c1-43af-9c2d-eb89e156734a/2965154a-b5d2-4479-ab85-6a8b06242014/j_c_hiatt.jpg", 32 | "image_path": "/prod/images/55d7f360-05c1-43af-9c2d-eb89e156734a/2965154a-b5d2-4479-ab85-6a8b06242014/j_c_hiatt.jpg", 33 | "id": "a", 34 | "href": "https://api.simplecast.com/episodes/39917452-ddc9-4b42-8471-a07fc4487277?preview=true", 35 | "guid": "a985054e-dc9b-45dd-93c9-3691c3690bec", 36 | "enclosure_url": "https://cl.ly/3ba8c8b8d9fe/Helping_Hands_ID_1202.mp3", 37 | "description": "Spicy jalapeno biltong kielbasa swine buffalo, prosciutto meatball shank. Tenderloin sirloin ground round, short ribs biltong cow tri-tip sausage buffalo andouille chicken turkey beef ribs shoulder. Tongue bresaola kielbasa picanha salami doner rump. Picanha corned beef tri-tip strip steak drumstick leberkas rump brisket pork loin sausage short ribs frankfurter. Hamburger jerky corned beef biltong kielbasa. Sirloin filet mignon shankle ribeye jerky pig. Spare ribs pork kielbasa tri-tip corned beef capicola.", 38 | "analytics": { 39 | "href": "https://api.simplecast.com/analytics/downloads?episode=39917452-ddc9-4b42-8471-a07fc4487277&preview=true" 40 | } 41 | }, 42 | { 43 | "updated_at": "2019-06-12T12:44:03.792833-07:00", 44 | "type": "full", 45 | "token": "cMNsoKRl", 46 | "title": "Podcast Episode Title", 47 | "status": "published", 48 | "slug": "podcast-episode-2", 49 | "season": { 50 | "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77", 51 | "number": 1 52 | }, 53 | "scheduled_for": null, 54 | "published_at": "2019-05-31T17:22:47.299272-07:00", 55 | "number": 7, 56 | "is_hidden": false, 57 | "image_url": "https://cdn.simplecast.com/images/55d7f360-05c1-43af-9c2d-eb89e156734a/3d52cc0a-c791-4b98-95d1-1b2e45b005b1/Jason_Avatar.jpg", 58 | "image_path": "/prod/images/55d7f360-05c1-43af-9c2d-eb89e156734a/3d52cc0a-c791-4b98-95d1-1b2e45b005b1/Jason_Avatar.jpg", 59 | "id": "b", 60 | "href": "https://api.simplecast.com/episodes/7979994f-c577-4728-aa5e-4f2c1e9314ae?preview=true", 61 | "guid": "39623c8c-1401-45e9-a803-382f9136eb26", 62 | "enclosure_url": "https://cl.ly/195b9e8b7c60/Lobo_Loco_-_10_-_Pianoman_Play_Sofa_Again_ID_1159.mp3", 63 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at sagittis felis. Quisque lacus sapien, consectetur quis nibh nec, dapibus euismod mi. Mauris vel luctus libero. Donec semper suscipit nibh eu mattis. Donec aliquet lacinia laoreet. Ut gravida hendrerit orci eu hendrerit. Nam vel tristique ex.", 64 | "analytics": { 65 | "href": "https://api.simplecast.com/analytics/downloads?episode=7979994f-c577-4728-aa5e-4f2c1e9314ae&preview=true" 66 | } 67 | }, 68 | { 69 | "updated_at": "2019-06-12T12:45:07.197070-07:00", 70 | "type": "full", 71 | "token": "edf78b39", 72 | "title": "Podcast Episode Title", 73 | "status": "published", 74 | "slug": "edf78b39", 75 | "season": { 76 | "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77", 77 | "number": 1 78 | }, 79 | "scheduled_for": null, 80 | "published_at": "2019-05-17T08:00:00.000000-07:00", 81 | "number": 6, 82 | "is_hidden": false, 83 | "image_url": "https://cdn.simplecast.com/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/815fd97a-c688-43a0-89ec-adb51771d245/1558126643artwork.jpg", 84 | "image_path": "/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/815fd97a-c688-43a0-89ec-adb51771d245/1558126643artwork.jpg", 85 | "id": "c", 86 | "href": "https://api.simplecast.com/episodes/815fd97a-c688-43a0-89ec-adb51771d245?preview=true", 87 | "guid": "471b8cf9-bd58-425a-9809-823c37e24389", 88 | "enclosure_url": "https://cl.ly/4a27ffd65e7c/Lobo_Loco_-_05_-_Overchill_ID_1140.mp3", 89 | "description": "Corned beef pork belly prosciutto tenderloin shank capicola. Swine spare ribs pork chop shank ham hock leberkas meatball. Prosciutto venison flank drumstick, bacon sausage pancetta leberkas meatloaf doner shoulder. Sausage ball tip pork chop alcatra, fatback brisket short loin.", 90 | "analytics": { 91 | "href": "https://api.simplecast.com/analytics/downloads?episode=815fd97a-c688-43a0-89ec-adb51771d245&preview=true" 92 | } 93 | }, 94 | { 95 | "updated_at": "2019-06-12T13:00:33.648782-07:00", 96 | "type": "full", 97 | "token": "b14002cc", 98 | "title": "Podcast Episode Title", 99 | "status": "published", 100 | "slug": "b14002cc", 101 | "season": { 102 | "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77", 103 | "number": 1 104 | }, 105 | "scheduled_for": null, 106 | "published_at": "2019-05-13T18:59:00.000000-07:00", 107 | "number": 5, 108 | "is_hidden": false, 109 | "image_url": "https://cdn.simplecast.com/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/831dd452-0017-431f-a6aa-e67f656bcbb5/1557799504artwork.jpg", 110 | "image_path": "/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/831dd452-0017-431f-a6aa-e67f656bcbb5/1557799504artwork.jpg", 111 | "id": "d", 112 | "href": "https://api.simplecast.com/episodes/831dd452-0017-431f-a6aa-e67f656bcbb5?preview=true", 113 | "guid": "38e0bb24-2c80-4448-8435-585ef9b87e97", 114 | "enclosure_url": "https://cl.ly/b0adbc9c58d4/Art_Of_Escapism_-_Dont_Feel_So_Low.mp3", 115 | "description": "Filet mignon leberkas meatball burgdoggen. Pastrami doner chuck, shank tenderloin ground round shankle ham hock burgdoggen cupim swine shoulder. Tenderloin burgdoggen turkey sausage, leberkas tail short ribs strip steak bacon filet mignon. Pork loin leberkas picanha swine.", 116 | "analytics": { 117 | "href": "https://api.simplecast.com/analytics/downloads?episode=831dd452-0017-431f-a6aa-e67f656bcbb5&preview=true" 118 | } 119 | }, 120 | { 121 | "updated_at": "2019-06-12T13:01:22.454517-07:00", 122 | "type": "full", 123 | "token": "e855d7bc", 124 | "title": "Podcast Episode Title", 125 | "status": "published", 126 | "slug": "e855d7bc", 127 | "season": { 128 | "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77", 129 | "number": 1 130 | }, 131 | "scheduled_for": null, 132 | "published_at": "2019-05-03T08:00:00.000000-07:00", 133 | "number": 4, 134 | "is_hidden": false, 135 | "image_url": "https://cdn.simplecast.com/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/27992848-007f-4c0e-b0b6-74518b758f7e/1556842744artwork.jpg", 136 | "image_path": "/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/27992848-007f-4c0e-b0b6-74518b758f7e/1556842744artwork.jpg", 137 | "id": "e", 138 | "href": "https://api.simplecast.com/episodes/27992848-007f-4c0e-b0b6-74518b758f7e?preview=true", 139 | "guid": "b61428c6-2974-4deb-a74f-6c22a9c2991f", 140 | "enclosure_url": "https://cl.ly/100c0e77180c/Ainst_Char_-_03_-_Your_Cellar_My_Shrine.mp3", 141 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at sagittis felis. Quisque lacus sapien, consectetur quis nibh nec, dapibus euismod mi. Mauris vel luctus libero. Donec semper suscipit nibh eu mattis. Donec aliquet lacinia laoreet. Ut gravida hendrerit orci eu hendrerit. Nam vel tristique ex.", 142 | "analytics": { 143 | "href": "https://api.simplecast.com/analytics/downloads?episode=27992848-007f-4c0e-b0b6-74518b758f7e&preview=true" 144 | } 145 | }, 146 | { 147 | "updated_at": "2019-06-12T13:01:58.802141-07:00", 148 | "type": "full", 149 | "token": "49266475", 150 | "title": "Podcast Episode Title", 151 | "status": "published", 152 | "slug": "49266475", 153 | "season": { 154 | "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77", 155 | "number": 1 156 | }, 157 | "scheduled_for": null, 158 | "published_at": "2019-04-29T16:19:00.000000-07:00", 159 | "number": 3, 160 | "is_hidden": false, 161 | "image_url": "https://cdn.simplecast.com/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4/1556580270artwork.jpg", 162 | "image_path": "/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4/1556580270artwork.jpg", 163 | "id": "f", 164 | "href": "https://api.simplecast.com/episodes/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4?preview=true", 165 | "guid": "5943566f-d1d5-48e1-9498-6972121ff6fd", 166 | "enclosure_url": "https://cl.ly/196987184667/Andrew_Walton_-_02_-_The_Curse_Of_The_Albatross.mp3", 167 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at sagittis felis. Quisque lacus sapien, consectetur quis nibh nec, dapibus euismod mi. Mauris vel luctus libero. Donec semper suscipit nibh eu mattis. Donec aliquet lacinia laoreet. Ut gravida hendrerit orci eu hendrerit. Nam vel tristique ex.", 168 | "analytics": { 169 | "href": "https://api.simplecast.com/analytics/downloads?episode=a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4&preview=true" 170 | } 171 | }, 172 | { 173 | "updated_at": "2019-06-12T13:01:58.802141-07:00", 174 | "type": "full", 175 | "token": "49266475", 176 | "title": "Podcast Episode Title", 177 | "status": "published", 178 | "slug": "49266475", 179 | "season": { 180 | "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77", 181 | "number": 1 182 | }, 183 | "scheduled_for": null, 184 | "published_at": "2019-04-29T16:19:00.000000-07:00", 185 | "number": 2, 186 | "is_hidden": false, 187 | "image_url": "https://cdn.simplecast.com/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4/1556580270artwork.jpg", 188 | "image_path": "/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4/1556580270artwork.jpg", 189 | "id": "g", 190 | "href": "https://api.simplecast.com/episodes/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4?preview=true", 191 | "guid": "5943566f-d1d5-48e1-9498-6972121ff6fd", 192 | "enclosure_url": "https://cl.ly/196987184667/Andrew_Walton_-_02_-_The_Curse_Of_The_Albatross.mp3", 193 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at sagittis felis. Quisque lacus sapien, consectetur quis nibh nec, dapibus euismod mi. Mauris vel luctus libero. Donec semper suscipit nibh eu mattis. Donec aliquet lacinia laoreet. Ut gravida hendrerit orci eu hendrerit. Nam vel tristique ex.", 194 | "analytics": { 195 | "href": "https://api.simplecast.com/analytics/downloads?episode=a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4&preview=true" 196 | } 197 | }, 198 | { 199 | "updated_at": "2019-06-12T13:01:58.802141-07:00", 200 | "type": "full", 201 | "token": "49266475", 202 | "title": "Podcast Episode Title", 203 | "status": "published", 204 | "slug": "49266475", 205 | "season": { 206 | "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77", 207 | "number": 1 208 | }, 209 | "scheduled_for": null, 210 | "published_at": "2019-04-29T16:19:00.000000-07:00", 211 | "number": 1, 212 | "is_hidden": false, 213 | "image_url": "https://cdn.simplecast.com/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4/1556580270artwork.jpg", 214 | "image_path": "/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4/1556580270artwork.jpg", 215 | "id": "h", 216 | "href": "https://api.simplecast.com/episodes/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4?preview=true", 217 | "guid": "5943566f-d1d5-48e1-9498-6972121ff6fd", 218 | "enclosure_url": "https://cl.ly/196987184667/Andrew_Walton_-_02_-_The_Curse_Of_The_Albatross.mp3", 219 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at sagittis felis. Quisque lacus sapien, consectetur quis nibh nec, dapibus euismod mi. Mauris vel luctus libero. Donec semper suscipit nibh eu mattis. Donec aliquet lacinia laoreet. Ut gravida hendrerit orci eu hendrerit. Nam vel tristique ex.", 220 | "analytics": { 221 | "href": "https://api.simplecast.com/analytics/downloads?episode=a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4&preview=true" 222 | } 223 | } 224 | ], 225 | "average_duration": 1994.26 226 | } 227 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/gatsby-browser.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Layout from "./src/components/layout" 3 | import Player from "./src/components/player" 4 | import { EpisodeProvider, EpisodeConsumer } from "./src/components/context" 5 | import { ThemeProvider, Styled } from "theme-ui" 6 | import theme from "./src/gatsby-plugin-theme-ui/index" 7 | import { SkipNavLink } from "@reach/skip-nav" 8 | 9 | export const wrapPageElement = ({ element, props }, options) => { 10 | const episodeSlug = options.episodeSlug ? options.episodeSlug : "show" 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | {props.location.pathname.includes(episodeSlug) || 18 | props.location.pathname === "/" ? ( 19 | 20 | {context => } 21 | 22 | ) : null} 23 | {element} 24 | 25 | 26 | 27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ markdownPath = `${__dirname}/content/episodes` }) => ({ 2 | siteMetadata: { 3 | title: `Podcast Name`, 4 | description: `Podcast description.`, 5 | author: `@vojtaholik`, 6 | }, 7 | plugins: [ 8 | `gatsby-plugin-react-helmet`, 9 | `gatsby-plugin-sharp`, 10 | `gatsby-transformer-remark`, 11 | `gatsby-transformer-sharp`, 12 | `gatsby-plugin-theme-ui`, 13 | { 14 | resolve: `gatsby-source-filesystem`, 15 | options: { 16 | name: `images`, 17 | path: `${__dirname}/src/images`, 18 | }, 19 | }, 20 | { 21 | resolve: `gatsby-source-filesystem`, 22 | options: { 23 | path: markdownPath, 24 | name: `episodes`, 25 | }, 26 | }, 27 | { 28 | resolve: `gatsby-plugin-manifest`, 29 | options: { 30 | name: `gatsby-theme-simplecast`, 31 | short_name: `simplecast`, 32 | start_url: `/`, 33 | background_color: `#A085FF`, 34 | theme_color: `#A085FF`, 35 | display: `minimal-ui`, 36 | icon: `src/images/icon.png`, // This path is relative to the root of the site. 37 | }, 38 | }, 39 | // this (optional) plugin enables Progressive Web App + Offline functionality 40 | // To learn more, visit: https://gatsby.dev/offline 41 | // `gatsby-plugin-offline`, 42 | ], 43 | }) 44 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/gatsby-node.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config({ 2 | path: `.env.${process.env.NODE_ENV}`, 3 | }) 4 | const axios = require("axios") 5 | const crypto = require("crypto") 6 | const path = require("path") 7 | const slugify = require("@sindresorhus/slugify") 8 | 9 | exports.sourceNodes = async ( 10 | { actions: { createNode, createNodeField }, plugins }, 11 | options 12 | ) => { 13 | axios.defaults.headers.common.Authorization = `Bearer ${options.simplecastApiSecret}` 14 | axios.defaults.headers.common.Accept = "application/json" 15 | 16 | const mockupEpisodes = require("./data/mockupEpisodes.json") 17 | 18 | const { data } = options.podcastId 19 | ? await axios( 20 | `https://api.simplecast.com/podcasts/${options.podcastId}/episodes` 21 | ) 22 | : mockupEpisodes 23 | 24 | const packagePodcast = p => { 25 | const nodeContent = JSON.stringify(p) 26 | const nodeContentDigest = crypto 27 | .createHash("md5") 28 | .update(nodeContent) 29 | .digest("hex") 30 | const node = { 31 | ...p, 32 | content: nodeContent, 33 | internal: { 34 | type: "Episode", 35 | contentDigest: nodeContentDigest, 36 | }, 37 | } 38 | 39 | createNode(node) 40 | } 41 | 42 | // fallback to mockup data if no podcast id provided 43 | 44 | options.podcastId 45 | ? data.collection.map(packagePodcast) 46 | : mockupEpisodes.collection.map(packagePodcast) 47 | } 48 | 49 | exports.createPages = async ({ actions, graphql }, options) => { 50 | const { data } = await graphql(` 51 | { 52 | site { 53 | siteMetadata { 54 | title 55 | } 56 | } 57 | allEpisode { 58 | edges { 59 | node { 60 | id 61 | title 62 | number 63 | } 64 | } 65 | } 66 | allMarkdownRemark { 67 | edges { 68 | node { 69 | id 70 | html 71 | frontmatter { 72 | title 73 | } 74 | } 75 | } 76 | } 77 | } 78 | `) 79 | 80 | data.allEpisode.edges.forEach(({ node }, options) => { 81 | actions.createPage({ 82 | path: `${options.episodeSlug ? options.episodeSlug : "show"}/${ 83 | node.number 84 | }/${slugify(node.title)}`, 85 | component: require.resolve(`./src/templates/episode.js`), 86 | context: { 87 | slug: slugify(node.title), 88 | id: node.id, 89 | title: node.title, 90 | }, 91 | }) 92 | }) 93 | } 94 | 95 | exports.onCreateNode = ({ node, getNode, actions }, options) => { 96 | const { createNodeField } = actions 97 | const showsSlug = options.episodeSlug ? options.episodeSlug : "show" 98 | createNodeField({ 99 | name: "slug", 100 | node, 101 | value: "/" + showsSlug + "/" + node.number + "/" + slugify(`${node.title}`), 102 | }) 103 | } 104 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/gatsby-ssr.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Layout from "./src/components/layout" 3 | import Player from "./src/components/player" 4 | import { EpisodeProvider, EpisodeConsumer } from "./src/components/context" 5 | import { ThemeProvider, Styled } from "theme-ui" 6 | import theme from "./src/gatsby-plugin-theme-ui/index" 7 | import { SkipNavLink } from "@reach/skip-nav" 8 | 9 | export const wrapPageElement = ({ element, props }, options) => { 10 | const episodeSlug = options.episodeSlug ? options.episodeSlug : "show" 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | {props.location.pathname.includes(episodeSlug) || 18 | props.location.pathname === "/" ? ( 19 | 20 | {context => } 21 | 22 | ) : null} 23 | {element} 24 | 25 | 26 | 27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/index.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { jsx } from "theme-ui" 3 | import { graphql } from "gatsby" 4 | import Episode from "gatsby-theme-simplecast/src/templates/episode" 5 | 6 | export default function Index({ data: { allEpisode, allMarkdownRemark } }) { 7 | const MarkdownForLatestEpisode = allMarkdownRemark.edges.filter( 8 | Markdown => Markdown.node.frontmatter.id === allEpisode.nodes[0].id 9 | ) 10 | 11 | const data = useStaticQuery(graphql` 12 | { 13 | allEpisode { 14 | totalCount 15 | nodes { 16 | id 17 | title 18 | description 19 | number 20 | enclosure_url 21 | fields { 22 | slug 23 | } 24 | } 25 | } 26 | allMarkdownRemark { 27 | edges { 28 | node { 29 | html 30 | frontmatter { 31 | id 32 | title 33 | resources 34 | guestSummary 35 | guestName 36 | guestPhoto { 37 | childImageSharp { 38 | fluid(maxWidth: 200) { 39 | ...GatsbyImageSharpFluid_noBase64 40 | } 41 | } 42 | } 43 | image { 44 | childImageSharp { 45 | original { 46 | src 47 | } 48 | fluid(maxWidth: 700) { 49 | ...GatsbyImageSharpFluid_noBase64 50 | } 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } 58 | `) 59 | return ( 60 | 67 | ) 68 | } 69 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vojtaholik/gatsby-theme-simplecast", 3 | "main": "index.js", 4 | "description": "Simplecast gatsby theme.", 5 | "version": "1.0.8", 6 | "author": "Vojta Holik ", 7 | "dependencies": { 8 | "@emotion/core": "^10.0.14", 9 | "@emotion/styled": "^10.0.14", 10 | "@mdx-js/react": "^1.0.27", 11 | "@reach/skip-nav": "^0.1.3", 12 | "@reach/visually-hidden": "^0.1.4", 13 | "@sindresorhus/slugify": "^0.9.1", 14 | "axios": "^0.19.0", 15 | "crypto": "^1.0.1", 16 | "dotenv": "^8.0.0", 17 | "fs": "^0.0.1-security", 18 | "gatsby": "^2.13.41", 19 | "gatsby-image": "^2.2.7", 20 | "gatsby-plugin-manifest": "^2.2.4", 21 | "gatsby-plugin-offline": "^2.2.4", 22 | "gatsby-plugin-react-helmet": "^3.1.2", 23 | "gatsby-plugin-sharp": "^2.2.9", 24 | "gatsby-plugin-theme-ui": "^0.2.18", 25 | "gatsby-source-filesystem": "^2.1.6", 26 | "gatsby-theme-ui": "^0.2.0", 27 | "gatsby-transformer-json": "^2.2.2", 28 | "gatsby-transformer-remark": "^2.6.9", 29 | "gatsby-transformer-sharp": "^2.2.4", 30 | "lodash": "^4.17.15", 31 | "prop-types": "^15.7.2", 32 | "react": "^16.8.6", 33 | "react-dom": "^16.8.6", 34 | "react-helmet": "^5.2.1", 35 | "react-icons": "^3.7.0", 36 | "react-markdown": "^4.1.0", 37 | "react-onclickoutside": "^6.8.0", 38 | "theme-ui": "^0.2.21" 39 | }, 40 | "devDependencies": { 41 | "gatsby": "^2.13.41", 42 | "prettier": "^1.18.2", 43 | "react": "^16.8.6", 44 | "react-dom": "^16.8.6", 45 | "theme-ui": "^0.2.21" 46 | }, 47 | "keywords": [ 48 | "gatsby", 49 | "gatsby-theme", 50 | "gatsby-plugin", 51 | "simplecast" 52 | ], 53 | "license": "MIT", 54 | "scripts": { 55 | "build": "gatsby build", 56 | "develop": "gatsby develop", 57 | "clean": "gatsby clean", 58 | "format": "prettier --write src/**/*.{js,jsx}", 59 | "start": "npm run develop", 60 | "serve": "gatsby serve", 61 | "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\"" 62 | }, 63 | "repository": { 64 | "type": "git", 65 | "url": "https://github.com/vojtaholik/gatsby-theme-simplecast" 66 | }, 67 | "bugs": { 68 | "url": "https://github.com/vojtaholik/gatsby-theme-simplecast/issues" 69 | }, 70 | "peerDependencies": { 71 | "gatsby": "^2.13.41", 72 | "react": "^16.8.6", 73 | "react-dom": "^16.8.6", 74 | "theme-ui": "^0.2.21" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/components/aside.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { jsx } from "theme-ui" 3 | import Img from "gatsby-image" 4 | import { FaExternalLinkAlt as ExternalLinkIcon } from "react-icons/fa" 5 | import config from "../lib/config" 6 | import { css } from "@emotion/core" 7 | import styled from "@emotion/styled" 8 | import Link from "./link" 9 | import Markdown from "react-markdown" 10 | import itunesIcon from "../images/apple.svg" 11 | import spotifyImage from "../images/spotify.png" 12 | import googleImage from "../images/google.svg" 13 | 14 | const PodcastProvider = styled(Link)( 15 | css({ 16 | mb: 5, 17 | display: "flex", 18 | alignItems: "center", 19 | img: { m: 0, mr: 3 }, 20 | }) 21 | ) 22 | 23 | function Aside({ markdown }) { 24 | return ( 25 | 90 | ) 91 | } 92 | 93 | export default Aside 94 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/components/bars.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | const Bars = () => ( 4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | ) 22 | 23 | export default Bars 24 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/components/context.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react" 2 | import { graphql, useStaticQuery } from "gatsby" 3 | 4 | const EpisodeContext = React.createContext() 5 | 6 | export function EpisodeProvider(props) { 7 | const data = useStaticQuery(graphql` 8 | { 9 | allEpisode { 10 | totalCount 11 | nodes { 12 | id 13 | title 14 | description 15 | number 16 | enclosure_url 17 | fields { 18 | slug 19 | } 20 | } 21 | } 22 | } 23 | `) 24 | 25 | const [currentPlaying, setCurrentPlaying] = React.useState( 26 | data.allEpisode.nodes[0] 27 | ) 28 | 29 | return ( 30 | 37 | ) 38 | } 39 | 40 | export class EpisodeConsumer extends Component { 41 | render() { 42 | return ( 43 | {this.props.children} 44 | ) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/components/header.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { jsx, useThemeUI, Header as ThemedHeader, Box, Flex } from "theme-ui" 3 | import Img from "gatsby-image" 4 | import { FaPlay as PlayIcon } from "react-icons/fa" 5 | import VisuallyHidden from "@reach/visually-hidden" 6 | import config from "../lib/config" 7 | 8 | function Header({ context, episode, image }) { 9 | const themeContext = useThemeUI() 10 | const { theme } = themeContext 11 | return ( 12 | 19 | {image && ( 20 | {episode.title} 25 | )} 26 | 27 | 36 | 37 | 41 |
42 |

{episode.title}

43 |
EP{episode.number}
44 |
45 |
46 |
47 |
48 |
49 | ) 50 | } 51 | 52 | export default Header 53 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/components/image.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { useStaticQuery, graphql } from "gatsby" 3 | import Img from "gatsby-image" 4 | 5 | /* 6 | * This component is built using `gatsby-image` to automatically serve optimized 7 | * images with lazy loading and reduced file sizes. The image is loaded using a 8 | * `useStaticQuery`, which allows us to load the image from directly within this 9 | * component, rather than having to pass the image data down from pages. 10 | * 11 | * For more information, see the docs: 12 | * - `gatsby-image`: https://gatsby.dev/gatsby-image 13 | * - `useStaticQuery`: https://www.gatsbyjs.org/docs/use-static-query/ 14 | */ 15 | 16 | const Image = () => { 17 | const data = useStaticQuery(graphql` 18 | query { 19 | placeholderImage: file(relativePath: { eq: "gatsby-astronaut.png" }) { 20 | childImageSharp { 21 | fluid(maxWidth: 300) { 22 | ...GatsbyImageSharpFluid 23 | } 24 | } 25 | } 26 | } 27 | `) 28 | 29 | return 30 | } 31 | 32 | export default Image 33 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/components/layout.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | -ms-text-size-adjust: 100%; 4 | -webkit-text-size-adjust: 100%; 5 | } 6 | body { 7 | margin: 0; 8 | -webkit-font-smoothing: antialiased; 9 | -moz-osx-font-smoothing: grayscale; 10 | } 11 | 12 | .progress { 13 | background: gray; 14 | /* height: 2rem; */ 15 | cursor: crosshair; 16 | overflow: hidden; 17 | } 18 | .progress__time { 19 | background: green; 20 | border-right: 1px solid rgba(0, 0, 0, 0.1); 21 | min-width: 5px; 22 | height: 100%; 23 | transition: width 0.1s; 24 | background: yellow; 25 | } 26 | 27 | article, 28 | aside, 29 | details, 30 | figcaption, 31 | figure, 32 | footer, 33 | header, 34 | main, 35 | menu, 36 | nav, 37 | section, 38 | summary { 39 | display: block; 40 | } 41 | audio, 42 | canvas, 43 | progress, 44 | video { 45 | display: inline-block; 46 | } 47 | audio:not([controls]) { 48 | display: none; 49 | height: 0; 50 | } 51 | progress { 52 | vertical-align: baseline; 53 | } 54 | [hidden], 55 | template { 56 | display: none; 57 | } 58 | a { 59 | background-color: transparent; 60 | -webkit-text-decoration-skip: objects; 61 | } 62 | a:active, 63 | a:hover { 64 | outline-width: 0; 65 | } 66 | abbr[title] { 67 | border-bottom: none; 68 | text-decoration: underline; 69 | text-decoration: underline dotted; 70 | } 71 | b, 72 | strong { 73 | font-weight: inherit; 74 | font-weight: bolder; 75 | } 76 | dfn { 77 | font-style: italic; 78 | } 79 | h1 { 80 | font-size: 2em; 81 | margin: 0.67em 0; 82 | } 83 | mark { 84 | background-color: #ff0; 85 | color: #000; 86 | } 87 | small { 88 | font-size: 80%; 89 | } 90 | sub, 91 | sup { 92 | font-size: 75%; 93 | line-height: 0; 94 | position: relative; 95 | vertical-align: baseline; 96 | } 97 | sub { 98 | bottom: -0.25em; 99 | } 100 | sup { 101 | top: -0.5em; 102 | } 103 | img { 104 | border-style: none; 105 | } 106 | svg:not(:root) { 107 | overflow: hidden; 108 | } 109 | code, 110 | kbd, 111 | pre, 112 | samp { 113 | font-family: monospace, monospace; 114 | font-size: 1em; 115 | } 116 | figure { 117 | margin: 1em 40px; 118 | } 119 | hr { 120 | box-sizing: content-box; 121 | height: 0; 122 | overflow: visible; 123 | } 124 | button, 125 | input, 126 | optgroup, 127 | select, 128 | textarea { 129 | font: inherit; 130 | margin: 0; 131 | } 132 | optgroup { 133 | font-weight: 700; 134 | } 135 | button, 136 | input { 137 | overflow: visible; 138 | } 139 | button, 140 | select { 141 | text-transform: none; 142 | } 143 | [type="reset"], 144 | [type="submit"], 145 | button, 146 | html [type="button"] { 147 | -webkit-appearance: button; 148 | } 149 | [type="button"]::-moz-focus-inner, 150 | [type="reset"]::-moz-focus-inner, 151 | [type="submit"]::-moz-focus-inner, 152 | button::-moz-focus-inner { 153 | border-style: none; 154 | padding: 0; 155 | } 156 | [type="button"]:-moz-focusring, 157 | [type="reset"]:-moz-focusring, 158 | [type="submit"]:-moz-focusring, 159 | button:-moz-focusring { 160 | outline: 1px dotted ButtonText; 161 | } 162 | fieldset { 163 | border: 1px solid silver; 164 | margin: 0 2px; 165 | padding: 0.35em 0.625em 0.75em; 166 | } 167 | legend { 168 | box-sizing: border-box; 169 | color: inherit; 170 | display: table; 171 | max-width: 100%; 172 | padding: 0; 173 | white-space: normal; 174 | } 175 | textarea { 176 | overflow: auto; 177 | } 178 | [type="checkbox"], 179 | [type="radio"] { 180 | box-sizing: border-box; 181 | padding: 0; 182 | } 183 | [type="number"]::-webkit-inner-spin-button, 184 | [type="number"]::-webkit-outer-spin-button { 185 | height: auto; 186 | } 187 | [type="search"] { 188 | -webkit-appearance: textfield; 189 | outline-offset: -2px; 190 | } 191 | [type="search"]::-webkit-search-cancel-button, 192 | [type="search"]::-webkit-search-decoration { 193 | -webkit-appearance: none; 194 | } 195 | ::-webkit-input-placeholder { 196 | color: inherit; 197 | opacity: 0.54; 198 | } 199 | ::-webkit-file-upload-button { 200 | -webkit-appearance: button; 201 | font: inherit; 202 | } 203 | html { 204 | font: 112.5%/1.45em georgia, serif; 205 | box-sizing: border-box; 206 | overflow-y: scroll; 207 | } 208 | * { 209 | box-sizing: inherit; 210 | } 211 | *:before { 212 | box-sizing: inherit; 213 | } 214 | *:after { 215 | box-sizing: inherit; 216 | } 217 | body { 218 | color: hsla(0, 0%, 0%, 0.8); 219 | font-family: georgia, serif; 220 | font-weight: normal; 221 | word-wrap: break-word; 222 | font-kerning: normal; 223 | -moz-font-feature-settings: "kern", "liga", "clig", "calt"; 224 | -ms-font-feature-settings: "kern", "liga", "clig", "calt"; 225 | -webkit-font-feature-settings: "kern", "liga", "clig", "calt"; 226 | font-feature-settings: "kern", "liga", "clig", "calt"; 227 | } 228 | img { 229 | max-width: 100%; 230 | margin-left: 0; 231 | margin-right: 0; 232 | margin-top: 0; 233 | padding-bottom: 0; 234 | padding-left: 0; 235 | padding-right: 0; 236 | padding-top: 0; 237 | margin-bottom: 1.45rem; 238 | } 239 | h1 { 240 | margin-left: 0; 241 | margin-right: 0; 242 | margin-top: 0; 243 | padding-bottom: 0; 244 | padding-left: 0; 245 | padding-right: 0; 246 | padding-top: 0; 247 | margin-bottom: 1.45rem; 248 | color: inherit; 249 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 250 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 251 | font-weight: bold; 252 | text-rendering: optimizeLegibility; 253 | font-size: 2.25rem; 254 | line-height: 1.1; 255 | } 256 | h2 { 257 | margin-left: 0; 258 | margin-right: 0; 259 | margin-top: 0; 260 | padding-bottom: 0; 261 | padding-left: 0; 262 | padding-right: 0; 263 | padding-top: 0; 264 | margin-bottom: 1.45rem; 265 | color: inherit; 266 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 267 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 268 | font-weight: bold; 269 | text-rendering: optimizeLegibility; 270 | font-size: 1.62671rem; 271 | line-height: 1.1; 272 | } 273 | h3 { 274 | margin-left: 0; 275 | margin-right: 0; 276 | margin-top: 0; 277 | padding-bottom: 0; 278 | padding-left: 0; 279 | padding-right: 0; 280 | padding-top: 0; 281 | margin-bottom: 1.45rem; 282 | color: inherit; 283 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 284 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 285 | font-weight: bold; 286 | text-rendering: optimizeLegibility; 287 | font-size: 1.38316rem; 288 | line-height: 1.1; 289 | } 290 | h4 { 291 | margin-left: 0; 292 | margin-right: 0; 293 | margin-top: 0; 294 | padding-bottom: 0; 295 | padding-left: 0; 296 | padding-right: 0; 297 | padding-top: 0; 298 | margin-bottom: 1.45rem; 299 | color: inherit; 300 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 301 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 302 | font-weight: bold; 303 | text-rendering: optimizeLegibility; 304 | font-size: 1rem; 305 | line-height: 1.1; 306 | } 307 | h5 { 308 | margin-left: 0; 309 | margin-right: 0; 310 | margin-top: 0; 311 | padding-bottom: 0; 312 | padding-left: 0; 313 | padding-right: 0; 314 | padding-top: 0; 315 | margin-bottom: 1.45rem; 316 | color: inherit; 317 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 318 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 319 | font-weight: bold; 320 | text-rendering: optimizeLegibility; 321 | font-size: 0.85028rem; 322 | line-height: 1.1; 323 | } 324 | h6 { 325 | margin-left: 0; 326 | margin-right: 0; 327 | margin-top: 0; 328 | padding-bottom: 0; 329 | padding-left: 0; 330 | padding-right: 0; 331 | padding-top: 0; 332 | margin-bottom: 1.45rem; 333 | color: inherit; 334 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 335 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 336 | font-weight: bold; 337 | text-rendering: optimizeLegibility; 338 | font-size: 0.78405rem; 339 | line-height: 1.1; 340 | } 341 | hgroup { 342 | margin-left: 0; 343 | margin-right: 0; 344 | margin-top: 0; 345 | padding-bottom: 0; 346 | padding-left: 0; 347 | padding-right: 0; 348 | padding-top: 0; 349 | margin-bottom: 1.45rem; 350 | } 351 | ul { 352 | list-style: none; 353 | margin-left: 0; 354 | margin-right: 0; 355 | margin-top: 0; 356 | padding-bottom: 0; 357 | padding-left: 0; 358 | padding-right: 0; 359 | padding-top: 0; 360 | margin-bottom: 1.45rem; 361 | list-style-position: outside; 362 | list-style-image: none; 363 | } 364 | ol { 365 | margin-left: 1.45rem; 366 | margin-right: 0; 367 | margin-top: 0; 368 | padding-bottom: 0; 369 | padding-left: 0; 370 | padding-right: 0; 371 | padding-top: 0; 372 | margin-bottom: 1.45rem; 373 | list-style-position: outside; 374 | list-style-image: none; 375 | } 376 | dl { 377 | margin-left: 0; 378 | margin-right: 0; 379 | margin-top: 0; 380 | padding-bottom: 0; 381 | padding-left: 0; 382 | padding-right: 0; 383 | padding-top: 0; 384 | margin-bottom: 1.45rem; 385 | } 386 | dd { 387 | margin-left: 0; 388 | margin-right: 0; 389 | margin-top: 0; 390 | padding-bottom: 0; 391 | padding-left: 0; 392 | padding-right: 0; 393 | padding-top: 0; 394 | margin-bottom: 1.45rem; 395 | } 396 | p { 397 | margin-left: 0; 398 | margin-right: 0; 399 | margin-top: 0; 400 | padding-bottom: 0; 401 | padding-left: 0; 402 | padding-right: 0; 403 | padding-top: 0; 404 | margin-bottom: 1.45rem; 405 | } 406 | figure { 407 | margin-left: 0; 408 | margin-right: 0; 409 | margin-top: 0; 410 | padding-bottom: 0; 411 | padding-left: 0; 412 | padding-right: 0; 413 | padding-top: 0; 414 | margin-bottom: 1.45rem; 415 | } 416 | pre { 417 | margin-left: 0; 418 | margin-right: 0; 419 | margin-top: 0; 420 | margin-bottom: 1.45rem; 421 | font-size: 0.85rem; 422 | line-height: 1.42; 423 | background: hsla(0, 0%, 0%, 0.04); 424 | border-radius: 3px; 425 | overflow: auto; 426 | word-wrap: normal; 427 | padding: 1.45rem; 428 | } 429 | table { 430 | margin-left: 0; 431 | margin-right: 0; 432 | margin-top: 0; 433 | padding-bottom: 0; 434 | padding-left: 0; 435 | padding-right: 0; 436 | padding-top: 0; 437 | margin-bottom: 1.45rem; 438 | font-size: 1rem; 439 | line-height: 1.45rem; 440 | border-collapse: collapse; 441 | width: 100%; 442 | } 443 | fieldset { 444 | margin-left: 0; 445 | margin-right: 0; 446 | margin-top: 0; 447 | padding-bottom: 0; 448 | padding-left: 0; 449 | padding-right: 0; 450 | padding-top: 0; 451 | margin-bottom: 1.45rem; 452 | } 453 | blockquote { 454 | margin-left: 1.45rem; 455 | margin-right: 1.45rem; 456 | margin-top: 0; 457 | padding-bottom: 0; 458 | padding-left: 0; 459 | padding-right: 0; 460 | padding-top: 0; 461 | margin-bottom: 1.45rem; 462 | } 463 | form { 464 | margin-left: 0; 465 | margin-right: 0; 466 | margin-top: 0; 467 | padding-bottom: 0; 468 | padding-left: 0; 469 | padding-right: 0; 470 | padding-top: 0; 471 | margin-bottom: 1.45rem; 472 | } 473 | noscript { 474 | margin-left: 0; 475 | margin-right: 0; 476 | margin-top: 0; 477 | padding-bottom: 0; 478 | padding-left: 0; 479 | padding-right: 0; 480 | padding-top: 0; 481 | margin-bottom: 1.45rem; 482 | } 483 | iframe { 484 | margin-left: 0; 485 | margin-right: 0; 486 | margin-top: 0; 487 | padding-bottom: 0; 488 | padding-left: 0; 489 | padding-right: 0; 490 | padding-top: 0; 491 | margin-bottom: 1.45rem; 492 | } 493 | hr { 494 | margin-left: 0; 495 | margin-right: 0; 496 | margin-top: 0; 497 | padding-bottom: 0; 498 | padding-left: 0; 499 | padding-right: 0; 500 | padding-top: 0; 501 | margin-bottom: calc(1.45rem - 1px); 502 | background: hsla(0, 0%, 0%, 0.2); 503 | border: none; 504 | height: 1px; 505 | } 506 | address { 507 | margin-left: 0; 508 | margin-right: 0; 509 | margin-top: 0; 510 | padding-bottom: 0; 511 | padding-left: 0; 512 | padding-right: 0; 513 | padding-top: 0; 514 | margin-bottom: 1.45rem; 515 | } 516 | b { 517 | font-weight: bold; 518 | } 519 | strong { 520 | font-weight: bold; 521 | } 522 | dt { 523 | font-weight: bold; 524 | } 525 | th { 526 | font-weight: bold; 527 | } 528 | li { 529 | margin-bottom: calc(1.45rem / 2); 530 | } 531 | ol li { 532 | padding-left: 0; 533 | } 534 | ul li { 535 | padding-left: 0; 536 | } 537 | li > ol { 538 | margin-left: 1.45rem; 539 | margin-bottom: calc(1.45rem / 2); 540 | margin-top: calc(1.45rem / 2); 541 | } 542 | li > ul { 543 | margin-left: 1.45rem; 544 | margin-bottom: calc(1.45rem / 2); 545 | margin-top: calc(1.45rem / 2); 546 | } 547 | blockquote *:last-child { 548 | margin-bottom: 0; 549 | } 550 | li *:last-child { 551 | margin-bottom: 0; 552 | } 553 | p *:last-child { 554 | margin-bottom: 0; 555 | } 556 | li > p { 557 | margin-bottom: calc(1.45rem / 2); 558 | } 559 | code { 560 | font-size: 0.85rem; 561 | line-height: 1.45rem; 562 | } 563 | kbd { 564 | font-size: 0.85rem; 565 | line-height: 1.45rem; 566 | } 567 | samp { 568 | font-size: 0.85rem; 569 | line-height: 1.45rem; 570 | } 571 | abbr { 572 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 573 | cursor: help; 574 | } 575 | acronym { 576 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 577 | cursor: help; 578 | } 579 | abbr[title] { 580 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 581 | cursor: help; 582 | text-decoration: none; 583 | } 584 | thead { 585 | text-align: left; 586 | } 587 | td, 588 | th { 589 | text-align: left; 590 | border-bottom: 1px solid hsla(0, 0%, 0%, 0.12); 591 | font-feature-settings: "tnum"; 592 | -moz-font-feature-settings: "tnum"; 593 | -ms-font-feature-settings: "tnum"; 594 | -webkit-font-feature-settings: "tnum"; 595 | padding-left: 0.96667rem; 596 | padding-right: 0.96667rem; 597 | padding-top: 0.725rem; 598 | padding-bottom: calc(0.725rem - 1px); 599 | } 600 | th:first-child, 601 | td:first-child { 602 | padding-left: 0; 603 | } 604 | th:last-child, 605 | td:last-child { 606 | padding-right: 0; 607 | } 608 | tt, 609 | code { 610 | background-color: hsla(0, 0%, 0%, 0.04); 611 | border-radius: 3px; 612 | font-family: "SFMono-Regular", Consolas, "Roboto Mono", "Droid Sans Mono", 613 | "Liberation Mono", Menlo, Courier, monospace; 614 | padding: 0; 615 | padding-top: 0.2em; 616 | padding-bottom: 0.2em; 617 | } 618 | pre code { 619 | background: none; 620 | line-height: 1.42; 621 | } 622 | code:before, 623 | code:after, 624 | tt:before, 625 | tt:after { 626 | letter-spacing: -0.2em; 627 | content: " "; 628 | } 629 | pre code:before, 630 | pre code:after, 631 | pre tt:before, 632 | pre tt:after { 633 | content: ""; 634 | } 635 | @media only screen and (max-width: 480px) { 636 | html { 637 | font-size: 100%; 638 | } 639 | } 640 | 641 | .bars { 642 | height: 45px; 643 | width: 0; 644 | position: relative; 645 | top: -9px; 646 | left: -1px; 647 | display: flex; 648 | flex-direction: column; 649 | align-items: flex-end; 650 | } 651 | 652 | .bar { 653 | background: #a085ff; 654 | bottom: 1px; 655 | height: 3px; 656 | position: absolute; 657 | width: 3px; 658 | animation: sound 0ms -800ms linear infinite alternate; 659 | } 660 | 661 | .bars--paused > * { 662 | animation-play-state: paused; 663 | } 664 | 665 | @keyframes sound { 666 | 0% { 667 | opacity: 0.35; 668 | background: #a085ff; 669 | width: 3px; 670 | } 671 | 100% { 672 | opacity: 1; 673 | background: #a085ff; 674 | width: 10px; 675 | } 676 | } 677 | 678 | .bar:nth-child(1) { 679 | top: 1px; 680 | animation-duration: 474ms; 681 | } 682 | .bar:nth-child(2) { 683 | top: 5px; 684 | animation-duration: 433ms; 685 | } 686 | .bar:nth-child(3) { 687 | top: 9px; 688 | animation-duration: 407ms; 689 | } 690 | .bar:nth-child(4) { 691 | top: 13px; 692 | animation-duration: 458ms; 693 | } 694 | .bar:nth-child(5) { 695 | top: 17px; 696 | animation-duration: 400ms; 697 | } 698 | .bar:nth-child(6) { 699 | top: 21px; 700 | animation-duration: 427ms; 701 | } 702 | .bar:nth-child(7) { 703 | top: 25px; 704 | animation-duration: 441ms; 705 | } 706 | .bar:nth-child(8) { 707 | top: 29px; 708 | animation-duration: 419ms; 709 | } 710 | .bar:nth-child(9) { 711 | top: 33px; 712 | animation-duration: 487ms; 713 | } 714 | 715 | .bar:nth-child(10) { 716 | top: 37px; 717 | animation-duration: 442ms; 718 | } 719 | 720 | .bar:nth-child(11) { 721 | top: 41px; 722 | animation-duration: 435ms; 723 | } 724 | 725 | .bar:nth-child(12) { 726 | top: 45px; 727 | animation-duration: 510ms; 728 | } 729 | 730 | .bar:nth-child(13) { 731 | top: 49px; 732 | animation-duration: 485ms; 733 | } 734 | 735 | .bar:nth-child(14) { 736 | top: 53px; 737 | animation-duration: 455ms; 738 | } 739 | 740 | .bar:nth-child(15) { 741 | top: 57px; 742 | animation-duration: 425ms; 743 | } 744 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/components/layout.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import PropTypes from "prop-types" 3 | import Navigation from "./navigation" 4 | import "./layout.css" 5 | import { jsx, Layout as Wrapper, Container } from "theme-ui" 6 | 7 | function Layout({ children }) { 8 | return ( 9 | 10 | 18 | 19 |
{children}
20 |
21 |
22 | ) 23 | } 24 | 25 | Layout.propTypes = { 26 | children: PropTypes.node.isRequired, 27 | } 28 | 29 | export default Layout 30 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/components/link.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import GatsbyLink from "gatsby-link" 3 | 4 | const Link = ({ children, to, ...other }) => { 5 | const internal = /^\/(?!\/)/.test(to) 6 | 7 | if (internal) { 8 | return ( 9 | 10 | {children} 11 | 12 | ) 13 | } 14 | 15 | return ( 16 | 17 | {children} 18 | 19 | ) 20 | } 21 | 22 | export default Link 23 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/components/navigation.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import React from "react" 3 | import { useStaticQuery, graphql } from "gatsby" 4 | import { jsx, Flex } from "theme-ui" 5 | import { EpisodeConsumer } from "./context" 6 | import { FaPlay as PlayIcon } from "react-icons/fa" 7 | import { MdMenu as MenuIcon, MdClose as CloseMenuIcon } from "react-icons/md" 8 | import onClickOutside from "react-onclickoutside" 9 | import config from "../lib/config" 10 | import Link from "./link" 11 | import Bars from "./bars" 12 | 13 | function Navigation() { 14 | const [isOpen, setIsOpen] = React.useState(false) 15 | const toggleMenu = () => setIsOpen(!isOpen) 16 | Navigation.handleClickOutside = () => setIsOpen(false) 17 | const twoDigits = n => (n.toString().length < 2 ? `0${n}` : n) 18 | 19 | const Logo = () => ( 20 | <> 21 | 22 |

23 | {data.site.siteMetadata.title 24 | ? data.site.siteMetadata.title 25 | : "Podcast Name"} 26 |

27 | 28 | {config.podcastSeason && ( 29 |
39 | season {twoDigits(config.podcastSeason)} 40 |
41 | )} 42 | 43 | ) 44 | 45 | const data = useStaticQuery(graphql` 46 | query navQuery { 47 | site { 48 | siteMetadata { 49 | title 50 | } 51 | } 52 | allEpisode { 53 | totalCount 54 | nodes { 55 | id 56 | title 57 | description 58 | number 59 | enclosure_url 60 | fields { 61 | slug 62 | } 63 | } 64 | } 65 | allMarkdownRemark { 66 | edges { 67 | node { 68 | id 69 | frontmatter { 70 | id 71 | summary 72 | } 73 | } 74 | } 75 | } 76 | } 77 | `) 78 | return ( 79 | 80 | {context => ( 81 | <> 82 | 87 | 92 | 93 | 94 | 112 | 113 | 158 | 159 | )} 160 | 161 | ) 162 | } 163 | 164 | const clickOutsideConfig = { 165 | handleClickOutside: () => Navigation.handleClickOutside, 166 | } 167 | 168 | export default onClickOutside(Navigation, clickOutsideConfig) 169 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/components/player.js: -------------------------------------------------------------------------------- 1 | // by Wes Bos, syntax.fm 2 | // https://github.com/wesbos/Syntax/blob/master/components/Player.js 3 | /** @jsx jsx */ 4 | import React from "react" 5 | import PropTypes from "prop-types" 6 | import { FaPlay, FaPause } from "react-icons/fa" 7 | import { jsx, Container } from "theme-ui" 8 | import { keyframes } from "@emotion/core" 9 | import formatTime from "../lib/formatTime" 10 | import VisuallyHidden from "@reach/visually-hidden" 11 | // import VolumeBars from "./volumeBars" 12 | 13 | export default class Player extends React.Component { 14 | static propTypes = { 15 | episode: PropTypes.object.isRequired, 16 | } 17 | 18 | constructor(props) { 19 | super(props) 20 | 21 | let lastPlayed = 0 22 | let lastVolumePref = 1 23 | // let lastPlaybackRate = 1 24 | 25 | // for Server Side Rendering 26 | if (typeof window !== "undefined") { 27 | const { episode } = this.props 28 | const lp = localStorage.getItem(`lastPlayed${episode.number}`) 29 | const lastVolume = localStorage.getItem(`lastVolumeSetting`) 30 | // const lastPlayback = localStorage.getItem(`lastPlaybackSetting`) 31 | 32 | if (lp) lastPlayed = JSON.parse(lp).lastPlayed 33 | if (lastVolume) lastVolumePref = JSON.parse(lastVolume).lastVolumePref 34 | // if (lastPlayback) 35 | // lastPlaybackRate = JSON.parse(lastPlayback).lastPlaybackRate 36 | } 37 | 38 | this.state = { 39 | progressTime: 50, 40 | playing: false, 41 | duration: 0, 42 | currentTime: lastPlayed, 43 | currentVolume: lastVolumePref, 44 | // playbackRate: lastPlaybackRate, 45 | timeWasLoaded: lastPlayed !== 0, 46 | showTooltip: false, 47 | tooltipPosition: 0, 48 | tooltipTime: "0:00", 49 | } 50 | } // END Constructor 51 | 52 | componentWillUpdate(nextProps, nextState) { 53 | // this.audio.playbackRate = nextState.playbackRate 54 | } 55 | 56 | componentDidUpdate(prevProps, prevState) { 57 | const { episode } = this.props 58 | const { 59 | currentTime, 60 | currentVolume, 61 | // playbackRate 62 | } = this.state 63 | if (episode.number !== prevProps.episode.number) { 64 | const lp = localStorage.getItem(`lastPlayed${episode.number}`) 65 | if (lp) { 66 | const lastVolume = localStorage.getItem(`lastVolumeSetting`) 67 | // const lastPlayback = localStorage.getItem(`lastPlaybackSetting`) 68 | const data = JSON.parse(lp) 69 | const data2 = JSON.parse(lastVolume) 70 | // const data3 = JSON.parse(lastPlayback) 71 | 72 | this.setState({ 73 | currentTime: data.lastPlayed, 74 | currentVolume: data2.lastVolumePref, 75 | // playbackRate: data3.lastPlaybackRate, 76 | }) 77 | this.audio.currentTime = data.lastPlayed 78 | this.audio.volume = data2.lastVolumePref 79 | // this.audio.playbackRate = data3.lastPlaybackRate 80 | } 81 | this.audio.play() 82 | } else { 83 | localStorage.setItem( 84 | `lastPlayed${episode.number}`, 85 | JSON.stringify({ lastPlayed: currentTime }) 86 | ) 87 | localStorage.setItem( 88 | `lastVolumeSetting`, 89 | JSON.stringify({ lastVolumePref: currentVolume }) 90 | ) 91 | // localStorage.setItem( 92 | // `lastPlaybackSetting`, 93 | // JSON.stringify({ lastPlaybackRate: playbackRate }) 94 | // ) 95 | } 96 | } 97 | 98 | timeUpdate = e => { 99 | // console.log('Updating Time'); 100 | const { episode } = this.props 101 | const { timeWasLoaded } = this.state 102 | // Check if the user already had a curent time 103 | if (timeWasLoaded) { 104 | const lp = localStorage.getItem(`lastPlayed${episode.number}`) 105 | 106 | if (lp) { 107 | e.currentTarget.currentTime = JSON.parse(lp).lastPlayed 108 | } 109 | this.setState({ timeWasLoaded: false }) 110 | } else { 111 | const { currentTime = 0, duration = 0 } = e.currentTarget 112 | 113 | const progressTime = (currentTime / duration) * 100 114 | if (Number.isNaN(progressTime)) return 115 | this.setState({ progressTime, currentTime, duration }) 116 | } 117 | } 118 | 119 | volumeUpdate = e => { 120 | const { timeWasLoaded } = this.state 121 | // Check if the user already had a curent volume 122 | if (timeWasLoaded) { 123 | const lastVolume = localStorage.getItem(`lastVolumeSetting`) 124 | if (lastVolume) { 125 | e.currentTarget.volume = JSON.parse(lastVolume).lastVolumePref 126 | } 127 | this.setState({ timeWasLoaded: false }) 128 | } 129 | } 130 | 131 | groupUpdates = e => { 132 | this.timeUpdate(e) 133 | this.volumeUpdate(e) 134 | } 135 | 136 | togglePlay = () => { 137 | const { playing } = this.state 138 | const method = playing ? "pause" : "play" 139 | this.audio[method]() 140 | } 141 | 142 | scrubTime = eventData => 143 | (eventData.nativeEvent.offsetX / this.progress.offsetWidth) * 144 | this.audio.duration 145 | 146 | scrub = e => { 147 | this.audio.currentTime = this.scrubTime(e) 148 | } 149 | 150 | seekTime = e => { 151 | this.setState({ 152 | tooltipPosition: e.nativeEvent.offsetX, 153 | tooltipTime: formatTime(this.scrubTime(e)), 154 | }) 155 | } 156 | 157 | playPause = () => { 158 | this.setState({ playing: !this.audio.paused }) 159 | const method = this.audio.paused ? "add" : "remove" 160 | document.querySelector(".bars").classList[method]("bars--paused") // 💩 161 | } 162 | 163 | volume = e => { 164 | this.audio.volume = e.currentTarget.value 165 | this.setState({ 166 | currentVolume: `${e.currentTarget.value}`, 167 | }) 168 | } 169 | 170 | speedUp = () => { 171 | this.speed(0.25) 172 | } 173 | 174 | speedDown = e => { 175 | e.preventDefault() 176 | this.speed(-0.25) 177 | } 178 | 179 | // speed = change => { 180 | // const playbackRateMax = 2.5 181 | // const playbackRateMin = 0.75 182 | 183 | // let playbackRate = this.state.playbackRate + change 184 | 185 | // if (playbackRate > playbackRateMax) { 186 | // playbackRate = playbackRateMin 187 | // } 188 | 189 | // if (playbackRate < playbackRateMin) { 190 | // playbackRate = playbackRateMax 191 | // } 192 | 193 | // this.setState({ playbackRate }) 194 | // } 195 | 196 | render() { 197 | const { episode } = this.props 198 | const { 199 | playing, 200 | // playbackRate, 201 | progressTime, 202 | currentTime, 203 | duration, 204 | showTooltip, 205 | tooltipPosition, 206 | tooltipTime, 207 | } = this.state 208 | 209 | const bounce = keyframes` 210 | from { 211 | transform: translateX(0) 212 | } 213 | to { 214 | transform: translateX(-200px) 215 | } 216 | ` 217 | 218 | return ( 219 |
237 | 246 |
257 | 285 |
315 |

316 | {episode.title} - EP{episode.number} 317 |

318 |
319 |
320 |
321 | 322 |
337 | {formatTime(currentTime)} 338 |
{ 351 | this.setState({ showTooltip: true }) 352 | }} 353 | onMouseLeave={() => { 354 | this.setState({ showTooltip: false }) 355 | }} 356 | ref={x => (this.progress = x)} 357 | > 358 | {/* eslint-enable */} 359 |
367 |
368 | {formatTime(duration)} 369 |
376 | {tooltipTime} 377 |
378 |
379 | 380 | {/*
381 | 390 |
*/} 391 | {/*
395 |

LOUDNESS

396 |
397 | 398 |
399 |
*/} 400 |
411 | ) 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/components/seo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SEO component that queries for data with 3 | * Gatsby's useStaticQuery React hook 4 | * 5 | * See: https://www.gatsbyjs.org/docs/use-static-query/ 6 | */ 7 | 8 | import React from "react" 9 | import PropTypes from "prop-types" 10 | import Helmet from "react-helmet" 11 | import { useStaticQuery, graphql } from "gatsby" 12 | 13 | function SEO({ description, lang, meta, title, image }) { 14 | const { site } = useStaticQuery( 15 | graphql` 16 | query { 17 | site { 18 | siteMetadata { 19 | title 20 | description 21 | author 22 | } 23 | } 24 | } 25 | ` 26 | ) 27 | 28 | const metaDescription = description || site.siteMetadata.description 29 | 30 | return ( 31 | 76 | ) 77 | } 78 | 79 | SEO.defaultProps = { 80 | lang: `en`, 81 | meta: [], 82 | description: ``, 83 | } 84 | 85 | SEO.propTypes = { 86 | description: PropTypes.string, 87 | lang: PropTypes.string, 88 | meta: PropTypes.arrayOf(PropTypes.object), 89 | title: PropTypes.string.isRequired, 90 | } 91 | 92 | export default SEO 93 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/components/volumeBars.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from "react" 2 | 3 | // TODO Fix all eslint issues 4 | 5 | // data generator -> to create 10 volume bars 6 | const getItems = count => { 7 | return Array.from({ length: count }, (v, i) => (i + 1) * 10).map(k => { 8 | let decimal = k / 100 9 | return { 10 | integer: `${k}`, 11 | deci: `${decimal}`, 12 | vol: `vol${k}`, 13 | level: `${k}/100`, 14 | checked: true, 15 | } 16 | }) // END MAP 17 | } // END ARROW 18 | 19 | class VolumeBars extends Component { 20 | state = { 21 | volumeBarList: getItems(10), 22 | } 23 | 24 | componentDidMount() { 25 | const localKey = `lastVolumeBarsOn` 26 | const localStorageRef = localStorage.getItem(localKey) 27 | if (localStorageRef) { 28 | this.setState({ volumeBarList: JSON.parse(localStorageRef) }) 29 | } 30 | } 31 | 32 | componentDidUpdate(prevProps, prevState) { 33 | const localKey = `lastVolumeBarsOn` 34 | const localValue = JSON.stringify(this.state.volumeBarList) 35 | localStorage.setItem(localKey, localValue) 36 | } 37 | 38 | //We are going to track which volume bars are "checked" 39 | handleOnClick = index => { 40 | // make a copy of state 41 | const volumeBarList = [...this.state.volumeBarList] 42 | // Get the index positions from 0 till index (index clicked) 43 | for (let i = 0; i <= index; i++) { 44 | volumeBarList[i].checked = true 45 | } 46 | // Get the index positions of the remaining non-checked 47 | for (let i = index + 1; i < 10; i++) { 48 | volumeBarList[i].checked = null 49 | } 50 | // Update State 51 | this.setState({ 52 | volumeBarList, 53 | }) 54 | } 55 | 56 | render() { 57 | return ( 58 | 59 | {this.state.volumeBarList.map((item, index) => ( 60 | 61 | { 63 | this.handleOnClick(index) 64 | }} 65 | onChange={this.props.volume} 66 | type="radio" 67 | name="volume" 68 | value={item.deci} 69 | id={item.vol} 70 | className="sr-only" 71 | /> 72 | 82 | 83 | ))} 84 | 85 | ) 86 | } 87 | } 88 | 89 | export default VolumeBars 90 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/gatsby-plugin-theme-ui/index.js: -------------------------------------------------------------------------------- 1 | import config from "../lib/config" 2 | 3 | export default { 4 | useCustomProperties: true, 5 | initialColorMode: "dark", 6 | breakpoints: ["992px", "1200px", "1920px"], 7 | space: [0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 56, 64, 72, 80, 128, 256, 512], 8 | sizes: [0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 56, 64, 72, 80, 128, 256, 512], 9 | fontSizes: [12, 14, 16, 18, 20, 22, 24, 32, 40, 48, 64], 10 | colors: { 11 | modes: { 12 | dark: { 13 | text: "rgba(255, 255, 255, 0.9)", 14 | background: "#1A2232", 15 | backgroundLighten10: "#232B3B", 16 | backgroundLighten20: "#2C3648", 17 | primaryDarken: "#7A5EFF", 18 | primary: "#A085FF", 19 | primaryLighten10: "#9D82FF", 20 | primaryLighten50: "#B298FF", 21 | primaryLighten70: "#D2C8FF", 22 | secondary: "#85FFD0", 23 | }, 24 | }, 25 | }, 26 | radii: [5, "50%"], 27 | fontWeights: { 28 | body: 300, 29 | heading: 500, 30 | }, 31 | lineHeights: { 32 | body: 1.675, 33 | heading: 1.125, 34 | }, 35 | letterSpacings: { 36 | heading: "1.5", 37 | }, 38 | fonts: { 39 | body: "system-ui, sans-serif", 40 | heading: "inherit", 41 | }, 42 | header: { 43 | logo: { 44 | flexDirection: "column", 45 | justifyContent: "center", 46 | a: { textDecoration: "none" }, 47 | container: { 48 | p: 3, 49 | display: ["flex", "none"], 50 | visibility: ["visible", "hidden"], 51 | width: "100%", 52 | justifyContent: "space-between", 53 | alignItems: "center", 54 | }, 55 | }, 56 | }, 57 | 58 | styles: { 59 | color: "primary", 60 | Header: { 61 | position: "relative", 62 | display: "flex", 63 | flexDirection: "column", 64 | width: "100%", 65 | height: config.headerImageHeight, 66 | color: "text", 67 | h1: { fontSize: [6, 8], textShadow: "0 2px 5px rgba(0,0,0,0.2)" }, 68 | "h1, h5": { m: 0 }, 69 | h5: { mt: 1, fontSize: 1, opacity: 0.6 }, 70 | ".header_content": { 71 | width: "100%", 72 | height: "100%", 73 | position: "absolute", 74 | //pb: [5, 8], 75 | px: [5, 8], 76 | zIndex: 1, 77 | display: "flex", 78 | alignItems: "flex-start", 79 | justifyContent: "flex-end", 80 | button: { 81 | width: "100%", 82 | maxWidth: 7, 83 | height: 7, 84 | background: "transparent", 85 | border: "1px solid", 86 | borderColor: "text", 87 | color: "text", 88 | fontSize: "10px", 89 | borderRadius: 1, 90 | display: "flex", 91 | alignItems: "center", 92 | justifyContent: "center", 93 | cursor: "pointer", 94 | mr: 3, 95 | mt: 2, 96 | svg: { 97 | mt: "1px", 98 | ml: "2px", 99 | }, 100 | }, 101 | }, 102 | }, 103 | root: { 104 | ".episodes_list": { 105 | backgroundColor: "background", 106 | position: ["absolute", "static"], 107 | zIndex: 2, 108 | width: "100%", 109 | maxWidth: [375, 300], 110 | px: 5, 111 | pt: 40, 112 | a: { 113 | textDecoration: "none", 114 | color: "text", 115 | fontSize: 3, 116 | fontWeight: "heading", 117 | }, 118 | li: { 119 | py: 0, 120 | display: "flex", 121 | justifyContent: "flex-start", 122 | alignItems: "center", 123 | ".summary": { 124 | fontSize: 2, 125 | lineHeight: 1.4, 126 | fontWeight: 300, 127 | opacity: 0.7, 128 | mt: 3, 129 | }, 130 | ".active": { 131 | borderLeft: "3px solid", 132 | borderColor: "primary", 133 | backgroundColor: "backgroundLighten10", 134 | }, 135 | a: { 136 | px: 5, 137 | py: 4, 138 | borderLeft: "3px solid", 139 | borderColor: "background", 140 | fontSize: 4, 141 | width: "100%", 142 | }, 143 | ":hover": { 144 | a: { borderColor: "backgroundLighten10" }, 145 | ".active": { 146 | borderColor: "primary", 147 | }, 148 | button: { 149 | opacity: 1, 150 | ":hover": { 151 | opacity: 1, 152 | }, 153 | }, 154 | }, 155 | h4: { 156 | mb: 0, 157 | }, 158 | button: { 159 | position: "absolute", 160 | opacity: 0, 161 | ml: -3, 162 | backgroundColor: "background", 163 | border: "1px solid", 164 | borderColor: "text", 165 | color: "text", 166 | display: "flex", 167 | width: "100%", 168 | maxWidth: "24px", 169 | height: "24px", 170 | flexGrow: "1", 171 | borderRadius: "50%", 172 | alignItems: "center", 173 | justifyContent: "center", 174 | svg: { mt: "1px", ml: "1px" }, 175 | cursor: "pointer", 176 | }, 177 | }, 178 | }, 179 | "[data-reach-skip-link]": { 180 | border: "0", 181 | clip: "rect(0 0 0 0)", 182 | height: "1px", 183 | width: "1px", 184 | margin: "-1px", 185 | padding: "0", 186 | overflow: "hidden", 187 | position: "absolute", 188 | zIndex: "999", 189 | }, 190 | "[data-reach-skip-link]:focus": { 191 | padding: "1rem", 192 | position: "fixed", 193 | top: "10px", 194 | left: "10px", 195 | backgroundColor: "background", 196 | width: "auto", 197 | height: "auto", 198 | clip: "auto", 199 | }, 200 | backgroundColor: "background", 201 | lineHeight: "body", 202 | fontFamily: "body", 203 | fontSize: [2, 3], 204 | color: "text", 205 | bg: "background", 206 | a: { 207 | color: "primaryLighten50", 208 | }, 209 | "a:hover": { 210 | color: "primaryLighten70", 211 | }, 212 | article: { 213 | p: [5, 8], 214 | pb: [2, 14], 215 | borderLeft: "2px solid", 216 | borderRight: "2px solid", 217 | borderColor: "backgroundLighten10", 218 | }, 219 | ".sidebar": { 220 | display: "flex", 221 | flexDirection: "column", 222 | p: [5, 8], 223 | pb: [13, 8], 224 | width: "100%", 225 | maxWidth: ["100%", 250], 226 | fontSize: "15px", 227 | h5: { my: 4, fontSize: 3 }, 228 | "h5:not(:first-of-type)": { mb: 10, mt: 0 }, 229 | ".guest": { 230 | fontSize: 1, 231 | textTransform: "uppercase", 232 | opacity: 0.8, 233 | fontWeight: "body", 234 | }, 235 | 236 | li: { 237 | mb: 2, 238 | display: "flex", 239 | a: { color: "text" }, 240 | svg: { 241 | mt: 1, 242 | mr: 1, 243 | width: "100%", 244 | maxWidth: 3, 245 | color: "text", 246 | opacity: 0.5, 247 | }, 248 | }, 249 | }, 250 | hr: { 251 | backgroundColor: "backgroundLighten10", 252 | height: "2px", 253 | }, 254 | }, 255 | Container: { 256 | maxWidth: 1200, 257 | }, 258 | a: { 259 | color: "primary", 260 | textDecoration: "none", 261 | ":hover": { 262 | color: "secondary", 263 | textDecoration: "underline", 264 | }, 265 | }, 266 | }, 267 | } 268 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/images/apple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/images/google.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vojtaholik/gatsby-theme-simplecast/2a67a6834ccb431886e7c0bcc9c7c8b4c1df4dfe/gatsby-theme-simplecast/src/images/icon.png -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/images/spotify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vojtaholik/gatsby-theme-simplecast/2a67a6834ccb431886e7c0bcc9c7c8b4c1df4dfe/gatsby-theme-simplecast/src/images/spotify.png -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/lib/config/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | podcastSeason: "1", 3 | headerImageHeight: [300, 400], 4 | spotifyUrl: "/", 5 | applePodcastsUrl: "/", 6 | googlePodcastsUrl: "/", 7 | } 8 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/lib/formatTime.js: -------------------------------------------------------------------------------- 1 | // TODO figure out the ~~ thing 2 | 3 | export default function formatTime(time) { 4 | // Hours, minutes and seconds 5 | const hrs = Math.floor(~~(time / 3600)) // eslint-disable-line 6 | const mins = Math.floor(~~((time % 3600) / 60)) // eslint-disable-line 7 | const secs = Math.floor(time % 60) 8 | 9 | // Output like "1:01" or "4:03:59" or "123:03:59" 10 | let ret = "" 11 | 12 | if (hrs > 0) { 13 | ret += `${hrs}:${mins < 10 ? "0" : ""}` 14 | } 15 | 16 | ret += `${mins}:${secs < 10 ? "0" : ""}` 17 | ret += `${secs}` 18 | return ret 19 | } 20 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import Layout from "../components/layout" 4 | import SEO from "../components/seo" 5 | 6 | const NotFoundPage = () => ( 7 | 8 | 9 |

NOT FOUND

10 |

You just hit a route that doesn't exist... the sadness.

11 |
12 | ) 13 | 14 | export default NotFoundPage 15 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/pages/index.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { jsx } from "theme-ui" 3 | import { graphql, useStaticQuery } from "gatsby" 4 | import Episode from "../templates/episode" 5 | 6 | export default function Index({ data: { allEpisode, allMarkdownRemark } }) { 7 | const MarkdownForLatestEpisode = allMarkdownRemark.edges.filter( 8 | Markdown => Markdown.node.frontmatter.id === allEpisode.nodes[0].id 9 | ) 10 | 11 | const data = useStaticQuery(graphql` 12 | { 13 | allEpisode { 14 | totalCount 15 | nodes { 16 | id 17 | title 18 | description 19 | number 20 | enclosure_url 21 | fields { 22 | slug 23 | } 24 | } 25 | } 26 | allMarkdownRemark { 27 | edges { 28 | node { 29 | html 30 | frontmatter { 31 | id 32 | title 33 | resources 34 | guestSummary 35 | guestName 36 | guestPhoto { 37 | childImageSharp { 38 | fluid(maxWidth: 200) { 39 | ...GatsbyImageSharpFluid_noBase64 40 | } 41 | } 42 | } 43 | image { 44 | childImageSharp { 45 | original { 46 | src 47 | } 48 | fluid(maxWidth: 700) { 49 | ...GatsbyImageSharpFluid_noBase64 50 | } 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } 58 | `) 59 | return ( 60 | 67 | ) 68 | } 69 | -------------------------------------------------------------------------------- /gatsby-theme-simplecast/src/templates/episode.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { jsx } from "theme-ui" 3 | import { graphql } from "gatsby" 4 | import { EpisodeConsumer } from "../components/context" 5 | import SEO from "../components/seo" 6 | import Header from "../components/header" 7 | import Aside from "../components/aside" 8 | import { SkipNavContent } from "@reach/skip-nav" 9 | 10 | function EpisodeTemplate({ data: { episode, markdownRemark } }) { 11 | const image = markdownRemark && markdownRemark.frontmatter.image 12 | const markdown = markdownRemark && markdownRemark 13 | 14 | return ( 15 | 16 | {context => ( 17 |
18 | 23 |
29 | 30 |
31 |
32 |

{episode.description && episode.description}

33 | {markdown && ( 34 |
35 | )} 36 |
37 | 38 |
40 |
41 | )} 42 |
43 | ) 44 | } 45 | 46 | export default EpisodeTemplate 47 | 48 | export const episodeQuery = graphql` 49 | query($id: String!) { 50 | episode(id: { eq: $id }) { 51 | id 52 | title 53 | description 54 | number 55 | enclosure_url 56 | fields { 57 | slug 58 | } 59 | } 60 | markdownRemark(frontmatter: { id: { eq: $id } }) { 61 | html 62 | frontmatter { 63 | id 64 | title 65 | resources 66 | guestName 67 | guestSummary 68 | guestPhoto { 69 | childImageSharp { 70 | fluid(maxWidth: 200) { 71 | ...GatsbyImageSharpFluid 72 | } 73 | } 74 | } 75 | image { 76 | childImageSharp { 77 | original { 78 | src 79 | } 80 | fluid(maxWidth: 700) { 81 | ...GatsbyImageSharpFluid 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | ` 89 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "gatsby-theme-simplecast", 5 | "demo" 6 | ] 7 | } 8 | --------------------------------------------------------------------------------