mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
69aabd1ae0
Summary: When video is opened from the app homepage, it opens in a popup, which stays open when it ends. The rel=0 parameter limits the related videos shown at the end to those from the same channel, avoiding surprising unrelated videos. This doesn't affect the video shown during initial onboarding, since that once auto-closes when it ends. Test Plan: Tested manually Reviewers: georgegevoian Reviewed By: georgegevoian Differential Revision: https://phab.getgrist.com/D4313
139 lines
3.3 KiB
TypeScript
139 lines
3.3 KiB
TypeScript
import {get as getBrowserGlobals} from 'app/client/lib/browserGlobals';
|
|
import {waitObs} from 'app/common/gutil';
|
|
import {Disposable, dom, DomElementArg} from 'grainjs';
|
|
import ko from 'knockout';
|
|
|
|
export interface Player {
|
|
playVideo(): void;
|
|
pauseVideo(): void;
|
|
stopVideo(): void;
|
|
mute(): void;
|
|
unMute(): void;
|
|
setVolume(volume: number): void;
|
|
getCurrentTime(): number;
|
|
getPlayerState(): PlayerState;
|
|
}
|
|
|
|
export interface PlayerOptions {
|
|
height?: string;
|
|
width?: string;
|
|
origin?: string;
|
|
playerVars?: PlayerVars;
|
|
onPlayerReady?(player: Player): void
|
|
onPlayerStateChange?(player: Player, event: PlayerStateChangeEvent): void;
|
|
}
|
|
|
|
export interface PlayerVars {
|
|
controls?: 0 | 1;
|
|
disablekb?: 0 | 1;
|
|
fs?: 0 | 1;
|
|
iv_load_policy?: 1 | 3;
|
|
modestbranding?: 0 | 1;
|
|
rel?: 0 | 1;
|
|
}
|
|
|
|
export interface PlayerStateChangeEvent {
|
|
data: PlayerState;
|
|
}
|
|
|
|
export enum PlayerState {
|
|
Unstarted = -1,
|
|
Ended = 0,
|
|
Playing = 1,
|
|
Paused = 2,
|
|
Buffering = 3,
|
|
VideoCued = 5,
|
|
}
|
|
|
|
const G = getBrowserGlobals('document', 'window');
|
|
|
|
/**
|
|
* Wrapper component for the YouTube IFrame Player API.
|
|
*
|
|
* Fetches the JavaScript code for the API if needed, and creates an iframe that
|
|
* points to a YouTube video with the specified id.
|
|
*
|
|
* For more documentation, see https://developers.google.com/youtube/iframe_api_reference.
|
|
*/
|
|
export class YouTubePlayer extends Disposable {
|
|
private _domArgs: DomElementArg[];
|
|
private _isLoading: ko.Observable<boolean> = ko.observable(true);
|
|
private _playerId = `youtube-player-${this._videoId}`;
|
|
private _player: Player;
|
|
|
|
constructor(
|
|
private _videoId: string,
|
|
private _options: PlayerOptions,
|
|
...domArgs: DomElementArg[]
|
|
) {
|
|
super();
|
|
|
|
this._domArgs = domArgs;
|
|
|
|
if (!G.window.YT) {
|
|
const tag = document.createElement('script');
|
|
|
|
tag.src = 'https://www.youtube.com/iframe_api';
|
|
const firstScriptTag = document.getElementsByTagName('script')[0];
|
|
firstScriptTag?.parentNode?.insertBefore(tag, firstScriptTag);
|
|
|
|
G.window.onYouTubeIframeAPIReady = () => this._handleYouTubeIframeAPIReady();
|
|
} else {
|
|
setTimeout(() => this._handleYouTubeIframeAPIReady(), 0);
|
|
}
|
|
}
|
|
|
|
public isLoading() {
|
|
return this._isLoading();
|
|
}
|
|
|
|
public isLoaded() {
|
|
return waitObs(this._isLoading, (val) => !val);
|
|
}
|
|
|
|
public play() {
|
|
this._player.playVideo();
|
|
}
|
|
|
|
public pause() {
|
|
this._player.pauseVideo();
|
|
}
|
|
|
|
public playPause() {
|
|
if (this._player.getPlayerState() === PlayerState.Playing) {
|
|
this._player.pauseVideo();
|
|
} else {
|
|
this._player.playVideo();
|
|
}
|
|
}
|
|
|
|
public setVolume(volume: number) {
|
|
this._player.setVolume(volume);
|
|
}
|
|
|
|
public getCurrentTime(): number {
|
|
return this._player.getCurrentTime();
|
|
}
|
|
|
|
public buildDom() {
|
|
return dom('div', {id: this._playerId}, ...this._domArgs);
|
|
}
|
|
|
|
private _handleYouTubeIframeAPIReady() {
|
|
const {onPlayerReady, onPlayerStateChange, playerVars, ...otherOptions} = this._options;
|
|
this._player = new G.window.YT.Player(this._playerId, {
|
|
videoId: this._videoId,
|
|
playerVars,
|
|
events: {
|
|
onReady: () => {
|
|
this._isLoading(false);
|
|
onPlayerReady?.(this._player);
|
|
},
|
|
onStateChange: (event: PlayerStateChangeEvent) =>
|
|
onPlayerStateChange?.(this._player, event),
|
|
},
|
|
...otherOptions,
|
|
});
|
|
}
|
|
}
|