Skip to content

Commit

Permalink
Merge pull request #245 from ericpp/live-episodes-podcast-page
Browse files Browse the repository at this point in the history
Added live episodes to podcast page
  • Loading branch information
daveajones authored Mar 24, 2023
2 parents 80b4335 + bb3cd71 commit 32d4c93
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 91 deletions.
14 changes: 11 additions & 3 deletions ui/src/components/EpisodeItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface IProps {
description?: string
datePublished?: number
hasComments: boolean
startTime?: number
onPlay?: any
onPause?: any
}
Expand Down Expand Up @@ -91,6 +92,7 @@ export default class EpisodeItem extends React.PureComponent<IProps> {
description,
datePublished,
hasComments,
startTime,
} = this.props
const episodeLink = link
const episodeEnclosure = enclosureUrl
Expand All @@ -113,9 +115,15 @@ export default class EpisodeItem extends React.PureComponent<IProps> {
<div className="episode-info">
<div className="episode-title">{title}</div>
<p className="episode-date">
<time dateTime={getISODate(datePublished)}>
{getPrettyDate(datePublished)}
</time>
{startTime ? (
<time dateTime={getISODate(startTime)}>
{getPrettyDate(startTime)}
</time>
) : (
<time dateTime={getISODate(datePublished)}>
{getPrettyDate(datePublished)}
</time>
)}
</p>

<div className="episode-links">
Expand Down
112 changes: 112 additions & 0 deletions ui/src/components/EpisodeList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import * as React from 'react'
import EpisodeItem from '../EpisodeItem'
import InfiniteList from '../InfiniteList'
import { fixURL } from '../../utils'

const he = require('he')

interface IProps {
title: string
podcast: any
episodes: Array<Object>
playingEpisode: Object
onEpisodePlay: Function
onEpisodePause: Function
}

export default class EpisodeList extends React.PureComponent<IProps> {
state = {
index: 0,
}
episodeItems: any[] = []

constructor(props) {
super(props)
this.onEpisodePlay = this.onEpisodePlay.bind(this)
this.onEpisodePause = this.onEpisodePause.bind(this)
this.renderEpisode = this.renderEpisode.bind(this)
}

async componentDidUpdate(prevProps) {
const { episodes, playingEpisode } = this.props

this.episodeItems.forEach((episodeItem, index) => {
episodeItem.current.setPlaying(
playingEpisode == episodes[index]
)
})
}

onEpisodePlay(index: number) {
// trigger the callback to play the episode
this.props.onEpisodePlay(this.props.episodes[index])
}

onEpisodePause() {
// trigger the callback to pause the episode
this.props.onEpisodePause()
}

renderEpisode(item, index: number) {
let {
id,
title,
image,
feedImage,
link,
enclosureUrl,
transcriptUrl,
description,
datePublished,
value,
socialInteract,
startTime
} = item
let {podcast} = this.props
// try to use episode image, fall back to feed images
image = image || feedImage || podcast.image || podcast.artwork
enclosureUrl = fixURL(enclosureUrl)
description = he.decode(description)

// create a reference to the generated EpisodeItem if one doesn't already exist
if (index >= this.episodeItems.length) {
this.episodeItems.push(React.createRef<EpisodeItem>())
}

return (
<div key={index}>
<EpisodeItem
ref={this.episodeItems[index]}
id={id}
index={index}
title={title}
image={image}
link={link}
value={value}
enclosureUrl={enclosureUrl}
transcriptUrl={transcriptUrl}
description={description}
datePublished={datePublished}
hasComments={socialInteract && socialInteract.length > 0}
startTime={startTime}
onPlay={this.onEpisodePlay}
onPause={this.onEpisodePause}
/>
</div>
)
}

render() {
const { title, episodes } = this.props

return (
<div className="episodes-list">
<h2 className="episode-header">{title}</h2>
<InfiniteList
data={episodes}
itemRenderer={this.renderEpisode}
/>
</div>
)
}
}
140 changes: 52 additions & 88 deletions ui/src/pages/Podcast/PodcastInfo/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import * as React from 'react'
import ReactLoading from 'react-loading'
import EpisodeItem from '../../../components/EpisodeItem'
import InfiniteList from '../../../components/InfiniteList'
import EpisodeList from '../../../components/EpisodeList'
import Player from '../../../components/Player'
import PodcastHeader from '../../../components/PodcastHeader'
import { fixURL, updateTitle } from '../../../utils'
import './styles.scss'

const he = require('he')

interface IProps {
match: any
result?: Object
Expand All @@ -17,9 +14,10 @@ interface IProps {
export default class PodcastInfo extends React.PureComponent<IProps> {
state = {
result: Object(),
episodes: [],
episodes: Object(),
loading: true,
selectedEpisode: undefined,
playingEpisode: undefined,
playing: false,
}
_isMounted = false
Expand All @@ -32,7 +30,6 @@ export default class PodcastInfo extends React.PureComponent<IProps> {
this.onEpisodePlay = this.onEpisodePlay.bind(this)
this.onEpisodePause = this.onEpisodePause.bind(this)
this.onEpisodeCanPlay = this.onEpisodeCanPlay.bind(this)
this.renderEpisode = this.renderEpisode.bind(this)
}

async componentDidMount(): Promise<void> {
Expand Down Expand Up @@ -60,13 +57,20 @@ export default class PodcastInfo extends React.PureComponent<IProps> {

async fetchData(id) {
const result = (await this.getPodcastInfo(id)).feed
const episodes: Array<any> = (await this.getEpisodes(id)).items
const episodes = (await this.getEpisodes(id))

if (this._isMounted) {
let selectedEpisode = episodes.items[0]

if (episodes.live.length > 0) {
selectedEpisode = episodes.live[0]
}

this.setState({
loading: false,
result,
episodes,
selectedEpisode: episodes[0],
selectedEpisode,
})
}
}
Expand All @@ -86,20 +90,29 @@ export default class PodcastInfo extends React.PureComponent<IProps> {
// credentials: 'same-origin',
method: 'GET',
})
return await response.json()

const episodes = await response.json()

return {
items: episodes.items,
live: episodes.liveItems.filter(
item => item.status === 'live'
),
}
}

onEpisodePlay(index: number) {
const { episodes, selectedEpisode } = this.state
onEpisodePlay(episode: object) {
const { selectedEpisode } = this.state

if (!episode) {
episode = selectedEpisode
}

this.setState({
playing: true,
playingEpisode: episode,
})

if (index === undefined) {
index = episodes.findIndex((x) => x === selectedEpisode)
}
const episode = episodes[index]

if (selectedEpisode !== episode) {
this.setState({
selectedEpisode: episode,
Expand All @@ -110,26 +123,15 @@ export default class PodcastInfo extends React.PureComponent<IProps> {
// and then check it when onCanPlay is triggered (handled by onEpisodeCanPlay) where the play call can be
// made again.
this.player.current.play()

// set all but current episode button to play; current to pause
this.episodeItems.forEach((episodeItem) => {
episodeItem.current.setPlaying(
index === episodeItem.current.props.index
)
})
}

onEpisodePause() {
this.setState({
playing: false,
playingEpisode: null,
})

this.player.current.pause()

// set episode buttons to play
this.episodeItems.forEach((episodeItem) => {
episodeItem.current.setPlaying(false)
})
}

onEpisodeCanPlay() {
Expand Down Expand Up @@ -181,11 +183,11 @@ export default class PodcastInfo extends React.PureComponent<IProps> {
}

renderPlayer() {
const { episodes, selectedEpisode, playing, result } = this.state
const { selectedEpisode, playing, result } = this.state
const preload = playing ? 'auto' : 'none'
return (
<div className="podcast-header-player">
{episodes.length > 0 ? (
{selectedEpisode ? (
<Player
ref={this.player}
episode={selectedEpisode}
Expand All @@ -203,67 +205,29 @@ export default class PodcastInfo extends React.PureComponent<IProps> {
}

renderEpisodes() {
const { episodes } = this.state
return (
<div className="episodes-list">
<h2 className="episode-header">Episodes</h2>
{episodes.length > 0 ? (
<InfiniteList
data={episodes}
itemRenderer={this.renderEpisode}
/>
) : (
<div>No episodes found</div>
)}
</div>
)
}

renderEpisode(item, index: number) {

let {
id,
title,
image,
feedImage,
link,
enclosureUrl,
transcriptUrl,
description,
datePublished,
value,
socialInteract
} = item
let {result} = this.state
// try to use episode image, fall back to feed images
image = image || feedImage || result.image || result.artwork
enclosureUrl = fixURL(enclosureUrl)
description = he.decode(description)

// create a reference to the generated EpisodeItem if one doesn't already exist
if (index >= this.episodeItems.length) {
this.episodeItems.push(React.createRef<EpisodeItem>())
}
const { result, episodes, playingEpisode } = this.state

return (
<div key={index}>
<EpisodeItem
ref={this.episodeItems[index]}
id={id}
index={index}
title={title}
image={image}
link={link}
value={value}
enclosureUrl={enclosureUrl}
transcriptUrl={transcriptUrl}
description={description}
datePublished={datePublished}
hasComments={socialInteract && socialInteract.length > 0}
onPlay={this.onEpisodePlay}
onPause={this.onEpisodePause}
<>
{episodes.live.length > 0 ? (
<EpisodeList
title="Live Now!"
podcast={result}
episodes={episodes.live}
playingEpisode={playingEpisode}
onEpisodePlay={this.onEpisodePlay}
onEpisodePause={this.onEpisodePause}
/>
) : ''}
<EpisodeList
title="Episodes"
podcast={result}
episodes={episodes.items}
playingEpisode={playingEpisode}
onEpisodePlay={this.onEpisodePlay}
onEpisodePause={this.onEpisodePause}
/>
</div>
</>
)
}

Expand Down

0 comments on commit 32d4c93

Please sign in to comment.