Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

All other loaded videos pause when one video is playing #1896

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions packages/obonode/obojobo-chunks-youtube/youtube-player.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const { uuid } = Common.util
// They'll all get called once it loads
const instanceCallbacksForYouTubeReady = []

const loadedVideos = {}

// single global hangler that notifies all registered YouTubePlayer Components
const onYouTubeIframeAPIReadyHandler = () => {
// call every registered callback when ready
Expand All @@ -22,6 +24,11 @@ class YouTubePlayer extends React.Component {
this.playerId = `obojobo-draft--chunks-you-tube-player-${uuid()}`
this.player = null
this.loadVideo = this.loadVideo.bind(this)
this.onStateChange = this.onStateChange.bind(this)

this.state = {
youTubePlayerId: null
}
}

componentDidMount() {
Expand All @@ -33,6 +40,10 @@ class YouTubePlayer extends React.Component {
}
}

componentWillUnmount() {
delete loadedVideos[this.state.youTubePlayerId]
}

componentDidUpdate(prevProps) {
// Don't update this component if the video properties haven't changed
if (!this.shouldVideoUpdate(prevProps)) {
Expand Down Expand Up @@ -79,8 +90,14 @@ class YouTubePlayer extends React.Component {
playerVars: {
start: startTime,
end: endTime
},
events: {
onStateChange: this.onStateChange
}
})
this.setState({ youTubePlayerId: this.player.id })

loadedVideos[this.player.id] = this.player
}

shouldVideoUpdate(prevProps) {
Expand All @@ -97,6 +114,18 @@ class YouTubePlayer extends React.Component {
render() {
return <div id={this.playerId} className="obojobo-draft--chunks-you-tube-player" />
}

onStateChange(playerState) {
switch (playerState.data) {
case 1: // video playing
Object.values(loadedVideos).forEach(video => {
if (video.id !== playerState.target.id) {
video.pauseVideo()
}
})
break
}
}
}

export default YouTubePlayer
82 changes: 80 additions & 2 deletions packages/obonode/obojobo-chunks-youtube/youtube-player.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ describe('YouTubePlayer', () => {
const tree = component.html()

expect(tree).toMatchSnapshot()

component.unmount()
})

test('YouTubePlayer updates video on content change', () => {
// Mock YouTube's Iframe Player object
window.YT = {
Player: jest.fn(() => ({
cueVideoById: jest.fn()
cueVideoById: jest.fn(),
pauseVideo: jest.fn()
}))
}

Expand Down Expand Up @@ -60,13 +63,15 @@ describe('YouTubePlayer', () => {
})

expect(spy).toHaveBeenCalledTimes(4)
spy.mockRestore()
})

test("YouTubePlayer doesn't update if content hasn't changed", () => {
window.YT = {
Player: jest.fn(() => ({
destroy: jest.fn(),
cueVideoById: jest.fn()
cueVideoById: jest.fn(),
pauseVideo: jest.fn()
}))
}

Expand All @@ -86,5 +91,78 @@ describe('YouTubePlayer', () => {

expect(spy).toHaveBeenCalledTimes(1)
expect(component.instance().player.destroy).not.toHaveBeenCalled()

spy.mockRestore()
})

test('Pause other players when a player is unpaused', () => {
const player1 = {
destroy: jest.fn(),
cueVideoById: jest.fn(),
id: 1,
pauseVideo: jest.fn()
}
const player2 = {
destroy: jest.fn(),
cueVideoById: jest.fn(),
id: 2,
pauseVideo: jest.fn()
}

const mockContent1 = {
videoId: 'mockIDzz',
startTime: 1,
endTime: 2,
playerState: 2
}
jest.mock('obojobo-document-engine/src/scripts/common/util/uuid', () => {
return () => 'mockId'
})
const mockContent2 = {
videoId: 'mockIDzz2',
startTime: 1,
endTime: 2,
playerState: 2
}

window.YT = {
Player: jest.fn(() => player1),
test: 123
}
const component1 = mount(<YouTubePlayer content={mockContent1} />)

window.YT = {
Player: jest.fn(() => player2),
test: 456
}
const component2 = mount(<YouTubePlayer content={mockContent2} />)

component1.instance().onStateChange({
data: 1,
target: player1
})
expect(player1.pauseVideo).not.toHaveBeenCalled()
expect(player2.pauseVideo).toHaveBeenCalledTimes(1)

component1.instance().onStateChange({
data: 1,
target: player2
})
expect(player2.pauseVideo).toHaveBeenCalledTimes(1)
expect(player1.pauseVideo).toHaveBeenCalledTimes(1)

component2.instance().onStateChange({
data: 1,
target: player1
})
expect(player1.pauseVideo).toHaveBeenCalledTimes(1)
expect(player2.pauseVideo).toHaveBeenCalledTimes(2)

component2.instance().onStateChange({
data: 1,
target: player2
})
expect(player1.pauseVideo).toHaveBeenCalledTimes(2)
expect(player2.pauseVideo).toHaveBeenCalledTimes(2)
})
})