Add Autoplay Next Button
This commit is contained in:
parent
bd64d5c9ea
commit
809136358b
6 changed files with 184 additions and 59 deletions
|
@ -11,16 +11,12 @@ import {
|
|||
import { doChangeVolume, doChangeMute, doAnalyticsView, doAnalyticsBuffer } from 'redux/actions/app';
|
||||
import { selectVolume, selectMute } from 'redux/selectors/app';
|
||||
import { savePosition, clearPosition, doSetPlayingUri, doPlayUri } from 'redux/actions/content';
|
||||
import {
|
||||
makeSelectContentPositionForUri,
|
||||
selectPlayingUri,
|
||||
makeSelectIsPlayerFloating,
|
||||
} from 'redux/selectors/content';
|
||||
import { makeSelectContentPositionForUri, selectPlayingUri, makeSelectIsPlayerFloating } from 'redux/selectors/content';
|
||||
import VideoViewer from './view';
|
||||
import { withRouter } from 'react-router';
|
||||
import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards';
|
||||
import { selectDaemonSettings, makeSelectClientSetting, selectHomepageData } from 'redux/selectors/settings';
|
||||
import { toggleVideoTheaterMode, doSetClientSetting } from 'redux/actions/settings';
|
||||
import { toggleVideoTheaterMode, toggleAutoplayNext, doSetClientSetting } from 'redux/actions/settings';
|
||||
import { selectUserVerifiedEmail, selectUser } from 'redux/selectors/user';
|
||||
|
||||
const select = (state, props) => {
|
||||
|
@ -71,6 +67,7 @@ const perform = (dispatch) => ({
|
|||
doAnalyticsBuffer: (uri, bufferData) => dispatch(doAnalyticsBuffer(uri, bufferData)),
|
||||
claimRewards: () => dispatch(doClaimEligiblePurchaseRewards()),
|
||||
toggleVideoTheaterMode: () => dispatch(toggleVideoTheaterMode()),
|
||||
toggleAutoplayNext: () => dispatch(toggleAutoplayNext()),
|
||||
setVideoPlaybackRate: (rate) => dispatch(doSetClientSetting(SETTINGS.VIDEO_PLAYBACK_RATE, rate)),
|
||||
doSetPlayingUri: (uri, collectionId) => dispatch(doSetPlayingUri({ uri, collectionId })),
|
||||
doPlayUri: (uri) => dispatch(doPlayUri(uri)),
|
||||
|
|
29
ui/component/viewers/videoViewer/internal/autoplay-next.js
Normal file
29
ui/component/viewers/videoViewer/internal/autoplay-next.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
// @flow
|
||||
import type { Player } from './videojs';
|
||||
import videojs from 'video.js';
|
||||
|
||||
class AutoplayNextButton extends videojs.getComponent('Button') {
|
||||
constructor(player, options = {}, autoplay) {
|
||||
super(player, options, autoplay);
|
||||
this.addClass(autoplay ? 'vjs-button--autoplay-next--active' : 'vjs-button--autoplay-next');
|
||||
this.controlText(autoplay ? 'Autoplay Next On' : 'Autoplay Next Off');
|
||||
}
|
||||
}
|
||||
|
||||
export function addAutoplayNextButton(player: Player, toggleAutoplayNext: () => void, autoplay: boolean) {
|
||||
const controlBar = player.getChild('controlBar');
|
||||
|
||||
const autoplayButton = new AutoplayNextButton(
|
||||
player,
|
||||
{
|
||||
name: 'AutoplayNextButton',
|
||||
text: __('Autoplay Next'),
|
||||
clickHandler: () => {
|
||||
toggleAutoplayNext();
|
||||
},
|
||||
},
|
||||
autoplay
|
||||
);
|
||||
|
||||
controlBar.addChild(autoplayButton);
|
||||
}
|
|
@ -53,6 +53,7 @@ type Props = {
|
|||
isAudio: boolean,
|
||||
startMuted: boolean,
|
||||
autoplay: boolean,
|
||||
autoplaySetting: boolean,
|
||||
toggleVideoTheaterMode: () => void,
|
||||
adUrl: ?string,
|
||||
claimId: ?string,
|
||||
|
@ -203,6 +204,7 @@ properties for this component should be kept to ONLY those that if changed shoul
|
|||
export default React.memo<Props>(function VideoJs(props: Props) {
|
||||
const {
|
||||
autoplay,
|
||||
autoplaySetting,
|
||||
startMuted,
|
||||
source,
|
||||
sourceType,
|
||||
|
@ -662,6 +664,24 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
|||
}
|
||||
}, [videoTheaterMode]);
|
||||
|
||||
useEffect(() => {
|
||||
const player = playerRef.current;
|
||||
if (player) {
|
||||
const controlBar = player.getChild('controlBar');
|
||||
const autoplayButton = controlBar.getChild('AutoplayNextButton');
|
||||
|
||||
if (autoplaySetting) {
|
||||
autoplayButton.removeClass('vjs-button--autoplay-next');
|
||||
autoplayButton.addClass('vjs-button--autoplay-next--active');
|
||||
autoplayButton.controlText('Autoplay Next On');
|
||||
} else {
|
||||
autoplayButton.removeClass('vjs-button--autoplay-next--active');
|
||||
autoplayButton.addClass('vjs-button--autoplay-next');
|
||||
autoplayButton.controlText('Autoplay Next Off');
|
||||
}
|
||||
}
|
||||
}, [autoplaySetting]);
|
||||
|
||||
// This lifecycle hook is only called once (on mount), or when `isAudio` changes.
|
||||
useEffect(() => {
|
||||
const vjsElement = createVideoPlayerDOM(containerRef.current);
|
||||
|
|
|
@ -16,6 +16,7 @@ import FileViewerEmbeddedEnded from 'web/component/fileViewerEmbeddedEnded';
|
|||
import FileViewerEmbeddedTitle from 'component/fileViewerEmbeddedTitle';
|
||||
import LoadingScreen from 'component/common/loading-screen';
|
||||
import { addTheaterModeButton } from './internal/theater-mode';
|
||||
import { addAutoplayNextButton } from './internal/autoplay-next';
|
||||
import { addPlayNextButton } from './internal/play-next';
|
||||
import { addPlayPreviousButton } from './internal/play-previous';
|
||||
import { useGetAds } from 'effects/use-get-ads';
|
||||
|
@ -51,6 +52,7 @@ type Props = {
|
|||
savePosition: (string, number) => void,
|
||||
clearPosition: (string) => void,
|
||||
toggleVideoTheaterMode: () => void,
|
||||
toggleAutoplayNext: () => void,
|
||||
setVideoPlaybackRate: (number) => void,
|
||||
doSetPlayingUri: (string, string) => void,
|
||||
doPlayUri: (string) => void,
|
||||
|
@ -92,6 +94,7 @@ function VideoViewer(props: Props) {
|
|||
clearPosition,
|
||||
desktopPlayStartTime,
|
||||
toggleVideoTheaterMode,
|
||||
toggleAutoplayNext,
|
||||
setVideoPlaybackRate,
|
||||
doSetPlayingUri,
|
||||
doPlayUri,
|
||||
|
@ -116,6 +119,7 @@ function VideoViewer(props: Props) {
|
|||
push,
|
||||
} = useHistory();
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [ended, setEnded] = useState(false);
|
||||
const [showAutoplayCountdown, setShowAutoplayCountdown] = useState(false);
|
||||
const [isEndededEmbed, setIsEndededEmbed] = useState(false);
|
||||
const vjsCallbackDataRef: any = React.useRef();
|
||||
|
@ -132,18 +136,21 @@ function VideoViewer(props: Props) {
|
|||
const [startPlayPrevious, setStartPlayPrevious] = useState(false);
|
||||
const [videoNode, setVideoNode] = useState(false);
|
||||
|
||||
const getNavigateUrl = React.useCallback((playUri: string) => {
|
||||
let navigateUrl;
|
||||
if (playUri) {
|
||||
navigateUrl = formatLbryUrlForWeb(playUri);
|
||||
if (collectionId) {
|
||||
const collectionParams = new URLSearchParams();
|
||||
collectionParams.set(COLLECTIONS_CONSTS.COLLECTION_ID, collectionId);
|
||||
navigateUrl = navigateUrl + `?` + collectionParams.toString();
|
||||
const getNavigateUrl = React.useCallback(
|
||||
(playUri: string) => {
|
||||
let navigateUrl;
|
||||
if (playUri) {
|
||||
navigateUrl = formatLbryUrlForWeb(playUri);
|
||||
if (collectionId) {
|
||||
const collectionParams = new URLSearchParams();
|
||||
collectionParams.set(COLLECTIONS_CONSTS.COLLECTION_ID, collectionId);
|
||||
navigateUrl = navigateUrl + `?` + collectionParams.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
return navigateUrl;
|
||||
}, [collectionId]);
|
||||
return navigateUrl;
|
||||
},
|
||||
[collectionId]
|
||||
);
|
||||
|
||||
// force everything to recent when URI changes, can cause weird corner cases otherwise (e.g. navigate while autoplay is true)
|
||||
useEffect(() => {
|
||||
|
@ -193,7 +200,19 @@ function VideoViewer(props: Props) {
|
|||
setStartPlayPrevious(false);
|
||||
}
|
||||
}
|
||||
}, [isFloating, push, doSetPlayingUri, playNextUri, doPlayUri, startPlayNext, collectionId, getNavigateUrl, videoNode, startPlayPrevious, playPreviousUri]);
|
||||
}, [
|
||||
isFloating,
|
||||
push,
|
||||
doSetPlayingUri,
|
||||
playNextUri,
|
||||
doPlayUri,
|
||||
startPlayNext,
|
||||
collectionId,
|
||||
getNavigateUrl,
|
||||
videoNode,
|
||||
startPlayPrevious,
|
||||
playPreviousUri,
|
||||
]);
|
||||
|
||||
function doTrackingBuffered(e: Event, data: any) {
|
||||
fetch(source, { method: 'HEAD', cache: 'no-store' }).then((response) => {
|
||||
|
@ -221,22 +240,34 @@ function VideoViewer(props: Props) {
|
|||
});
|
||||
}
|
||||
|
||||
const onEnded = React.useCallback(() => {
|
||||
analytics.videoIsPlaying(false);
|
||||
React.useEffect(() => {
|
||||
if (ended) {
|
||||
analytics.videoIsPlaying(false);
|
||||
|
||||
if (adUrl) {
|
||||
setAdUrl(null);
|
||||
return;
|
||||
if (adUrl) {
|
||||
setAdUrl(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (embedded) {
|
||||
setIsEndededEmbed(true);
|
||||
} else if (autoplaySetting) {
|
||||
setShowAutoplayCountdown(true);
|
||||
}
|
||||
|
||||
clearPosition(uri);
|
||||
}
|
||||
|
||||
if (embedded) {
|
||||
setIsEndededEmbed(true);
|
||||
} else if (autoplaySetting) {
|
||||
setShowAutoplayCountdown(true);
|
||||
}
|
||||
|
||||
clearPosition(uri);
|
||||
}, [embedded, setIsEndededEmbed, autoplaySetting, setShowAutoplayCountdown, adUrl, setAdUrl, clearPosition, uri]);
|
||||
}, [
|
||||
embedded,
|
||||
setIsEndededEmbed,
|
||||
autoplaySetting,
|
||||
setShowAutoplayCountdown,
|
||||
adUrl,
|
||||
setAdUrl,
|
||||
clearPosition,
|
||||
uri,
|
||||
ended,
|
||||
]);
|
||||
|
||||
function onPlay(player) {
|
||||
setIsLoading(false);
|
||||
|
@ -283,6 +314,8 @@ function VideoViewer(props: Props) {
|
|||
if (collectionId) {
|
||||
addPlayNextButton(player, () => setStartPlayNext(true));
|
||||
addPlayPreviousButton(player, () => setStartPlayPrevious(true));
|
||||
} else {
|
||||
addAutoplayNextButton(player, toggleAutoplayNext, autoplaySetting);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -319,7 +352,7 @@ function VideoViewer(props: Props) {
|
|||
|
||||
// first play tracking, used for initializing the watchman api
|
||||
player.on('tracking:firstplay', doTrackingFirstPlay);
|
||||
player.on('ended', onEnded);
|
||||
player.on('ended', () => setEnded(true));
|
||||
player.on('play', onPlay);
|
||||
player.on('pause', (event) => onPause(event, player));
|
||||
player.on('dispose', (event) => onDispose(event, player));
|
||||
|
@ -407,6 +440,7 @@ function VideoViewer(props: Props) {
|
|||
startMuted={autoplayIfEmbedded}
|
||||
toggleVideoTheaterMode={toggleVideoTheaterMode}
|
||||
autoplay={!embedded || autoplayIfEmbedded}
|
||||
autoplaySetting={autoplaySetting}
|
||||
claimId={claimId}
|
||||
userId={userId}
|
||||
allowPreRoll={!embedded && !authenticated}
|
||||
|
|
|
@ -18,8 +18,8 @@ export const IS_MAC = process.platform === 'darwin';
|
|||
const UPDATE_IS_NIGHT_INTERVAL = 5 * 60 * 1000;
|
||||
|
||||
export function doFetchDaemonSettings() {
|
||||
return dispatch => {
|
||||
Lbry.settings_get().then(settings => {
|
||||
return (dispatch) => {
|
||||
Lbry.settings_get().then((settings) => {
|
||||
analytics.toggleInternal(settings.share_usage_data);
|
||||
dispatch({
|
||||
type: ACTIONS.DAEMON_SETTINGS_RECEIVED,
|
||||
|
@ -32,11 +32,11 @@ export function doFetchDaemonSettings() {
|
|||
}
|
||||
|
||||
export function doFindFFmpeg() {
|
||||
return dispatch => {
|
||||
return (dispatch) => {
|
||||
dispatch({
|
||||
type: LOCAL_ACTIONS.FINDING_FFMPEG_STARTED,
|
||||
});
|
||||
return Lbry.ffmpeg_find().then(done => {
|
||||
return Lbry.ffmpeg_find().then((done) => {
|
||||
dispatch(doGetDaemonStatus());
|
||||
dispatch({
|
||||
type: LOCAL_ACTIONS.FINDING_FFMPEG_COMPLETED,
|
||||
|
@ -46,8 +46,8 @@ export function doFindFFmpeg() {
|
|||
}
|
||||
|
||||
export function doGetDaemonStatus() {
|
||||
return dispatch => {
|
||||
return Lbry.status().then(status => {
|
||||
return (dispatch) => {
|
||||
return Lbry.status().then((status) => {
|
||||
dispatch({
|
||||
type: ACTIONS.DAEMON_STATUS_RECEIVED,
|
||||
data: {
|
||||
|
@ -72,7 +72,7 @@ export function doClearDaemonSetting(key) {
|
|||
key,
|
||||
};
|
||||
// not if syncLocked
|
||||
Lbry.settings_clear(clearKey).then(defaultSettings => {
|
||||
Lbry.settings_clear(clearKey).then((defaultSettings) => {
|
||||
if (SDK_SYNC_KEYS.includes(key)) {
|
||||
dispatch({
|
||||
type: ACTIONS.SHARED_PREFERENCE_SET,
|
||||
|
@ -83,7 +83,7 @@ export function doClearDaemonSetting(key) {
|
|||
dispatch(doWalletReconnect());
|
||||
}
|
||||
});
|
||||
Lbry.settings_get().then(settings => {
|
||||
Lbry.settings_get().then((settings) => {
|
||||
analytics.toggleInternal(settings.share_usage_data);
|
||||
dispatch({
|
||||
type: ACTIONS.DAEMON_SETTINGS_RECEIVED,
|
||||
|
@ -108,7 +108,7 @@ export function doSetDaemonSetting(key, value, doNotDispatch = false) {
|
|||
key,
|
||||
value: !value && value !== false ? null : value,
|
||||
};
|
||||
Lbry.settings_set(newSettings).then(newSetting => {
|
||||
Lbry.settings_set(newSettings).then((newSetting) => {
|
||||
if (SDK_SYNC_KEYS.includes(key) && !doNotDispatch) {
|
||||
dispatch({
|
||||
type: ACTIONS.SHARED_PREFERENCE_SET,
|
||||
|
@ -121,7 +121,7 @@ export function doSetDaemonSetting(key, value, doNotDispatch = false) {
|
|||
// todo: add sdk reloadsettings() (or it happens automagically?)
|
||||
}
|
||||
});
|
||||
Lbry.settings_get().then(settings => {
|
||||
Lbry.settings_get().then((settings) => {
|
||||
analytics.toggleInternal(settings.share_usage_data);
|
||||
dispatch({
|
||||
type: ACTIONS.DAEMON_SETTINGS_RECEIVED,
|
||||
|
@ -170,7 +170,7 @@ export function doUpdateIsNight() {
|
|||
}
|
||||
|
||||
export function doUpdateIsNightAsync() {
|
||||
return dispatch => {
|
||||
return (dispatch) => {
|
||||
dispatch(doUpdateIsNight());
|
||||
|
||||
setInterval(() => dispatch(doUpdateIsNight()), UPDATE_IS_NIGHT_INTERVAL);
|
||||
|
@ -201,8 +201,8 @@ export function doSetDarkTime(value, options) {
|
|||
|
||||
export function doGetWalletSyncPreference() {
|
||||
const SYNC_KEY = 'enable-sync';
|
||||
return dispatch => {
|
||||
return Lbry.preference_get({ key: SYNC_KEY }).then(result => {
|
||||
return (dispatch) => {
|
||||
return Lbry.preference_get({ key: SYNC_KEY }).then((result) => {
|
||||
const enabled = result && result[SYNC_KEY];
|
||||
if (enabled !== null) {
|
||||
dispatch(doSetClientSetting(SETTINGS.ENABLE_SYNC, enabled));
|
||||
|
@ -214,8 +214,8 @@ export function doGetWalletSyncPreference() {
|
|||
|
||||
export function doSetWalletSyncPreference(pref) {
|
||||
const SYNC_KEY = 'enable-sync';
|
||||
return dispatch => {
|
||||
return Lbry.preference_set({ key: SYNC_KEY, value: pref }).then(result => {
|
||||
return (dispatch) => {
|
||||
return Lbry.preference_set({ key: SYNC_KEY, value: pref }).then((result) => {
|
||||
const enabled = result && result[SYNC_KEY];
|
||||
if (enabled !== null) {
|
||||
dispatch(doSetClientSetting(SETTINGS.ENABLE_SYNC, enabled));
|
||||
|
@ -226,7 +226,7 @@ export function doSetWalletSyncPreference(pref) {
|
|||
}
|
||||
|
||||
export function doPushSettingsToPrefs() {
|
||||
return dispatch => {
|
||||
return (dispatch) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
dispatch({
|
||||
type: LOCAL_ACTIONS.SYNC_CLIENT_SETTINGS,
|
||||
|
@ -274,8 +274,8 @@ export function doFetchLanguage(language) {
|
|||
if (settings.language !== language || (settings.loadedLanguages && !settings.loadedLanguages.includes(language))) {
|
||||
// this should match the behavior/logic in index-web.html
|
||||
fetch('https://lbry.com/i18n/get/lbry-desktop/app-strings/' + language + '.json')
|
||||
.then(r => r.json())
|
||||
.then(j => {
|
||||
.then((r) => r.json())
|
||||
.then((j) => {
|
||||
window.i18n_messages[language] = j;
|
||||
dispatch({
|
||||
type: LOCAL_ACTIONS.DOWNLOAD_LANGUAGE_SUCCESS,
|
||||
|
@ -284,7 +284,7 @@ export function doFetchLanguage(language) {
|
|||
},
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
.catch((e) => {
|
||||
dispatch({
|
||||
type: LOCAL_ACTIONS.DOWNLOAD_LANGUAGE_FAILURE,
|
||||
});
|
||||
|
@ -324,8 +324,8 @@ export function doSetLanguage(language) {
|
|||
) {
|
||||
// this should match the behavior/logic in index-web.html
|
||||
fetch('https://lbry.com/i18n/get/lbry-desktop/app-strings/' + language + '.json')
|
||||
.then(r => r.json())
|
||||
.then(j => {
|
||||
.then((r) => r.json())
|
||||
.then((j) => {
|
||||
window.i18n_messages[language] = j;
|
||||
dispatch({
|
||||
type: LOCAL_ACTIONS.DOWNLOAD_LANGUAGE_SUCCESS,
|
||||
|
@ -344,7 +344,7 @@ export function doSetLanguage(language) {
|
|||
});
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
.catch((e) => {
|
||||
window.localStorage.setItem(SETTINGS.LANGUAGE, DEFAULT_LANGUAGE);
|
||||
dispatch(doSetClientSetting(SETTINGS.LANGUAGE, DEFAULT_LANGUAGE));
|
||||
const languageName = SUPPORTED_LANGUAGES[language] ? SUPPORTED_LANGUAGES[language] : language;
|
||||
|
@ -369,7 +369,7 @@ export function doSetAutoLaunch(value) {
|
|||
}
|
||||
|
||||
if (value === undefined) {
|
||||
launcher.isEnabled().then(isEnabled => {
|
||||
launcher.isEnabled().then((isEnabled) => {
|
||||
if (isEnabled) {
|
||||
if (!autoLaunch) {
|
||||
launcher.disable().then(() => {
|
||||
|
@ -385,7 +385,7 @@ export function doSetAutoLaunch(value) {
|
|||
}
|
||||
});
|
||||
} else if (value === true) {
|
||||
launcher.isEnabled().then(function(isEnabled) {
|
||||
launcher.isEnabled().then(function (isEnabled) {
|
||||
if (!isEnabled) {
|
||||
launcher.enable().then(() => {
|
||||
dispatch(doSetClientSetting(SETTINGS.AUTO_LAUNCH, true));
|
||||
|
@ -396,7 +396,7 @@ export function doSetAutoLaunch(value) {
|
|||
});
|
||||
} else {
|
||||
// value = false
|
||||
launcher.isEnabled().then(function(isEnabled) {
|
||||
launcher.isEnabled().then(function (isEnabled) {
|
||||
if (isEnabled) {
|
||||
launcher.disable().then(() => {
|
||||
dispatch(doSetClientSetting(SETTINGS.AUTO_LAUNCH, false));
|
||||
|
@ -410,7 +410,7 @@ export function doSetAutoLaunch(value) {
|
|||
}
|
||||
|
||||
export function doSetAppToTrayWhenClosed(value) {
|
||||
return dispatch => {
|
||||
return (dispatch) => {
|
||||
window.localStorage.setItem(SETTINGS.TO_TRAY_WHEN_CLOSED, value);
|
||||
dispatch(doSetClientSetting(SETTINGS.TO_TRAY_WHEN_CLOSED, value));
|
||||
};
|
||||
|
@ -424,3 +424,12 @@ export function toggleVideoTheaterMode() {
|
|||
dispatch(doSetClientSetting(SETTINGS.VIDEO_THEATER_MODE, !videoTheaterMode));
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleAutoplayNext() {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const autoplayNext = makeSelectClientSetting(SETTINGS.AUTOPLAY)(state);
|
||||
|
||||
dispatch(doSetClientSetting(SETTINGS.AUTOPLAY, !autoplayNext));
|
||||
};
|
||||
}
|
||||
|
|
|
@ -132,6 +132,42 @@
|
|||
}
|
||||
}
|
||||
|
||||
.vjs-button--autoplay-next.vjs-button {
|
||||
@media (min-width: $breakpoint-medium) {
|
||||
display: block;
|
||||
order: 1;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-toggle-left'%3e%3crect x='1' y='5' width='22' height='14' rx='7' ry='7'%3e%3c/rect%3e%3ccircle cx='8' cy='12' r='3'%3e%3c/circle%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
&:focus:not(:focus-visible) {
|
||||
// Need to repeat these styles because of video.js weirdness
|
||||
// see: https://github.com/lbryio/lbry-desktop/pull/5549#discussion_r580406932
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-toggle-left'%3e%3crect x='1' y='5' width='22' height='14' rx='7' ry='7'%3e%3c/rect%3e%3ccircle cx='8' cy='12' r='3'%3e%3c/circle%3e%3c/svg%3e");
|
||||
}
|
||||
}
|
||||
|
||||
.vjs-button--autoplay-next--active.vjs-button {
|
||||
@media (min-width: $breakpoint-medium) {
|
||||
display: block;
|
||||
order: 1;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-toggle-right'%3e%3crect x='1' y='5' width='22' height='14' rx='7' ry='7'%3e%3c/rect%3e%3ccircle cx='16' cy='12' r='3'%3e%3c/circle%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
&:focus:not(:focus-visible) {
|
||||
// Need to repeat these styles because of video.js weirdness
|
||||
// see: https://github.com/lbryio/lbry-desktop/pull/5549#discussion_r580406932
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-toggle-right'%3e%3crect x='1' y='5' width='22' height='14' rx='7' ry='7'%3e%3c/rect%3e%3ccircle cx='16' cy='12' r='3'%3e%3c/circle%3e%3c/svg%3e");
|
||||
}
|
||||
}
|
||||
|
||||
.vjs-button--play-previous.vjs-button {
|
||||
@media (min-width: $breakpoint-medium) {
|
||||
display: block;
|
||||
|
|
Loading…
Add table
Reference in a new issue