├── .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 |
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 |
410 |
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 |
19 |
--------------------------------------------------------------------------------
/gatsby-theme-simplecast/src/images/google.svg:
--------------------------------------------------------------------------------
1 |
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 |
39 |
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 |
--------------------------------------------------------------------------------