File Page and Player style changes on mobile view
- Biggest change: Moved mobile player logic outside of fileRenderFloating into its own component fileRenderMobile, since there is no need for all that extra resizing and dragging code (for now, as mobile doesn't have a floating player) - Moved player to the header height - Removed rounded borders and margins
This commit is contained in:
parent
0c47f1daa9
commit
8c3e376873
11 changed files with 366 additions and 26 deletions
|
@ -15,6 +15,7 @@ import { openContextMenu } from 'util/context-menu';
|
|||
import useKonamiListener from 'util/enhanced-layout';
|
||||
import Yrbl from 'component/yrbl';
|
||||
import FileRenderFloating from 'component/fileRenderFloating';
|
||||
import FileRenderMobile from 'component/fileRenderMobile';
|
||||
import { withRouter } from 'react-router';
|
||||
import usePrevious from 'effects/use-previous';
|
||||
import Nag from 'component/common/nag';
|
||||
|
@ -518,7 +519,7 @@ function App(props: Props) {
|
|||
<Router />
|
||||
<ModalRouter />
|
||||
<React.Suspense fallback={null}>{renderFiledrop && <FileDrop />}</React.Suspense>
|
||||
<FileRenderFloating />
|
||||
{isMobile ? <FileRenderMobile /> : <FileRenderFloating />}
|
||||
<React.Suspense fallback={null}>
|
||||
{isEnhancedLayout && <Yrbl className="yrbl--enhanced" />}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doUriInitiatePlay } from 'redux/actions/content';
|
||||
import { selectThumbnailForUri, makeSelectClaimWasPurchased } from 'redux/selectors/claims';
|
||||
import { doUriInitiatePlay, doSetPlayingUri } from 'redux/actions/content';
|
||||
import { selectThumbnailForUri, selectClaimForUri, makeSelectClaimWasPurchased } from 'redux/selectors/claims';
|
||||
import { makeSelectFileInfoForUri } from 'redux/selectors/file_info';
|
||||
import * as SETTINGS from 'constants/settings';
|
||||
import { selectCostInfoForUri } from 'lbryinc';
|
||||
|
@ -14,10 +14,16 @@ import {
|
|||
makeSelectFileRenderModeForUri,
|
||||
} from 'redux/selectors/content';
|
||||
import FileRenderInitiator from './view';
|
||||
import { getChannelIdFromClaim } from 'util/claim';
|
||||
import { selectActiveLivestreamForChannel } from 'redux/selectors/livestream';
|
||||
|
||||
const select = (state, props) => {
|
||||
const { uri } = props;
|
||||
|
||||
const claim = selectClaimForUri(state, uri);
|
||||
const claimId = claim && claim.claim_id;
|
||||
const channelClaimId = claim && getChannelIdFromClaim(claim);
|
||||
|
||||
return {
|
||||
claimThumbnail: selectThumbnailForUri(state, uri),
|
||||
fileInfo: makeSelectFileInfoForUri(uri)(state),
|
||||
|
@ -29,11 +35,14 @@ const select = (state, props) => {
|
|||
renderMode: makeSelectFileRenderModeForUri(uri)(state),
|
||||
claimWasPurchased: makeSelectClaimWasPurchased(uri)(state),
|
||||
authenticated: selectUserVerifiedEmail(state),
|
||||
activeLivestreamForChannel: channelClaimId && selectActiveLivestreamForChannel(state, channelClaimId),
|
||||
claimId,
|
||||
};
|
||||
};
|
||||
|
||||
const perform = {
|
||||
doUriInitiatePlay,
|
||||
doSetPlayingUri,
|
||||
};
|
||||
|
||||
export default withRouter(connect(select, perform)(FileRenderInitiator));
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// The actual viewer for a file exists in TextViewer and FileRenderFloating
|
||||
// They can't exist in one component because we need to handle/listen for the start of a new file view
|
||||
// while a file is currently being viewed
|
||||
import { useIsMobile } from 'effects/use-screensize';
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import * as PAGES from 'constants/pages';
|
||||
|
@ -30,7 +31,10 @@ type Props = {
|
|||
claimWasPurchased: boolean,
|
||||
authenticated: boolean,
|
||||
videoTheaterMode: boolean,
|
||||
activeLivestreamForChannel?: any,
|
||||
claimId?: string,
|
||||
doUriInitiatePlay: (uri: string, collectionId: ?string, isPlayable: boolean) => void,
|
||||
doSetPlayingUri: ({ uri: ?string }) => void,
|
||||
};
|
||||
|
||||
export default function FileRenderInitiator(props: Props) {
|
||||
|
@ -49,11 +53,16 @@ export default function FileRenderInitiator(props: Props) {
|
|||
claimWasPurchased,
|
||||
authenticated,
|
||||
videoTheaterMode,
|
||||
activeLivestreamForChannel,
|
||||
claimId,
|
||||
doUriInitiatePlay,
|
||||
doSetPlayingUri,
|
||||
} = props;
|
||||
|
||||
const containerRef = React.useRef<any>();
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const [thumbnail, setThumbnail] = React.useState(FileRenderPlaceholder);
|
||||
|
||||
const { search, href, state: locationState } = location;
|
||||
|
@ -69,11 +78,27 @@ export default function FileRenderInitiator(props: Props) {
|
|||
const canViewFile = isFree || claimWasPurchased;
|
||||
const isPlayable = RENDER_MODES.FLOATING_MODES.includes(renderMode);
|
||||
const isText = RENDER_MODES.TEXT_MODES.includes(renderMode);
|
||||
const isCurrentClaimLive = activeLivestreamForChannel && claimId && activeLivestreamForChannel.claimId === claimId;
|
||||
const isMobileClaimLive = isMobile && isCurrentClaimLive;
|
||||
const foundCover = thumbnail !== FileRenderPlaceholder;
|
||||
|
||||
const renderUnsupported = RENDER_MODES.UNSUPPORTED_IN_THIS_APP.includes(renderMode);
|
||||
const disabled = renderUnsupported || (!fileInfo && insufficientCredits && !claimWasPurchased);
|
||||
const shouldRedirect = !authenticated && !isFree;
|
||||
|
||||
React.useEffect(() => {
|
||||
// Set livestream as playing uri so it can be rendered by <FileRenderMobile />
|
||||
// instead of showing an empty cover image. Needs cover to fill the space with the player.
|
||||
if (isMobileClaimLive && foundCover) {
|
||||
doSetPlayingUri({ uri });
|
||||
}
|
||||
|
||||
// No floating player on mobile as of now, so clear the playing uri
|
||||
if (isMobile && (isPlayable || isMobileClaimLive)) {
|
||||
return () => doSetPlayingUri({ uri: null });
|
||||
}
|
||||
}, [doSetPlayingUri, foundCover, isMobile, isMobileClaimLive, isPlayable, uri]);
|
||||
|
||||
function doAuthRedirect() {
|
||||
history.push(`/$/${PAGES.AUTH}?redirect=${encodeURIComponent(location.pathname)}`);
|
||||
}
|
||||
|
@ -128,7 +153,7 @@ export default function FileRenderInitiator(props: Props) {
|
|||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
onClick={disabled ? undefined : shouldRedirect ? doAuthRedirect : viewFile}
|
||||
onClick={disabled || isMobileClaimLive ? undefined : shouldRedirect ? doAuthRedirect : viewFile}
|
||||
style={thumbnail && !obscurePreview ? { backgroundImage: `url("${thumbnail}")` } : {}}
|
||||
className={classnames('content__cover', {
|
||||
'content__cover--disabled': disabled,
|
||||
|
@ -158,7 +183,7 @@ export default function FileRenderInitiator(props: Props) {
|
|||
)
|
||||
)}
|
||||
|
||||
{!disabled && (
|
||||
{!disabled && !isMobileClaimLive && (
|
||||
<Button
|
||||
requiresAuth={shouldRedirect}
|
||||
onClick={viewFile}
|
||||
|
|
44
ui/component/fileRenderMobile/index.js
Normal file
44
ui/component/fileRenderMobile/index.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { makeSelectClaimWasPurchased, selectClaimForUri } from 'redux/selectors/claims';
|
||||
import { makeSelectStreamingUrlForUri } from 'redux/selectors/file_info';
|
||||
import {
|
||||
makeSelectNextUrlForCollectionAndUrl,
|
||||
makeSelectPreviousUrlForCollectionAndUrl,
|
||||
} from 'redux/selectors/collections';
|
||||
import { selectPlayingUri, makeSelectFileRenderModeForUri } from 'redux/selectors/content';
|
||||
import { selectCostInfoForUri } from 'lbryinc';
|
||||
import { doPlayUri } from 'redux/actions/content';
|
||||
import { withRouter } from 'react-router';
|
||||
import { getChannelIdFromClaim } from 'util/claim';
|
||||
import { selectActiveLivestreamForChannel } from 'redux/selectors/livestream';
|
||||
import FileRenderMobile from './view';
|
||||
|
||||
const select = (state, props) => {
|
||||
const playingUri = selectPlayingUri(state);
|
||||
const uri = playingUri && playingUri.uri;
|
||||
const collectionId = playingUri && playingUri.collectionId;
|
||||
|
||||
const claim = selectClaimForUri(state, uri);
|
||||
const claimId = claim && claim.claim_id;
|
||||
const channelClaimId = claim && getChannelIdFromClaim(claim);
|
||||
|
||||
return {
|
||||
uri,
|
||||
streamingUrl: makeSelectStreamingUrlForUri(uri)(state),
|
||||
renderMode: makeSelectFileRenderModeForUri(uri)(state),
|
||||
costInfo: selectCostInfoForUri(state, uri),
|
||||
claimWasPurchased: makeSelectClaimWasPurchased(uri)(state),
|
||||
nextListUri: collectionId && makeSelectNextUrlForCollectionAndUrl(collectionId, uri)(state),
|
||||
previousListUri: collectionId && makeSelectPreviousUrlForCollectionAndUrl(collectionId, uri)(state),
|
||||
collectionId,
|
||||
activeLivestreamForChannel: channelClaimId && selectActiveLivestreamForChannel(state, channelClaimId),
|
||||
claimId,
|
||||
channelClaimId,
|
||||
};
|
||||
};
|
||||
|
||||
const perform = {
|
||||
doPlayUri,
|
||||
};
|
||||
|
||||
export default withRouter(connect(select, perform)(FileRenderMobile));
|
177
ui/component/fileRenderMobile/view.jsx
Normal file
177
ui/component/fileRenderMobile/view.jsx
Normal file
|
@ -0,0 +1,177 @@
|
|||
// @flow
|
||||
import * as RENDER_MODES from 'constants/file_render_modes';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { onFullscreenChange } from 'util/full-screen';
|
||||
import { generateListSearchUrlParams, formatLbryUrlForWeb } from 'util/url';
|
||||
import { useHistory } from 'react-router';
|
||||
import LoadingScreen from 'component/common/loading-screen';
|
||||
import FileRender from 'component/fileRender';
|
||||
import AutoplayCountdown from 'component/autoplayCountdown';
|
||||
import LivestreamIframeRender from 'component/livestreamLayout/iframe-render';
|
||||
|
||||
const PRIMARY_PLAYER_WRAPPER_CLASS = 'file-page__video-container';
|
||||
export const INLINE_PLAYER_WRAPPER_CLASS = 'inline-player__wrapper';
|
||||
|
||||
// ****************************************************************************
|
||||
// ****************************************************************************
|
||||
|
||||
type Props = {
|
||||
claimId?: string,
|
||||
uri: string,
|
||||
streamingUrl?: string,
|
||||
renderMode: string,
|
||||
collectionId: string,
|
||||
costInfo: any,
|
||||
claimWasPurchased: boolean,
|
||||
nextListUri: string,
|
||||
previousListUri: string,
|
||||
activeLivestreamForChannel?: any,
|
||||
channelClaimId?: any,
|
||||
doPlayUri: (string) => void,
|
||||
};
|
||||
|
||||
export default function FileRenderMobile(props: Props) {
|
||||
const {
|
||||
claimId,
|
||||
uri,
|
||||
streamingUrl,
|
||||
renderMode,
|
||||
collectionId,
|
||||
costInfo,
|
||||
claimWasPurchased,
|
||||
nextListUri,
|
||||
previousListUri,
|
||||
activeLivestreamForChannel,
|
||||
channelClaimId,
|
||||
doPlayUri,
|
||||
} = props;
|
||||
|
||||
const { push } = useHistory();
|
||||
|
||||
const [fileViewerRect, setFileViewerRect] = useState();
|
||||
const [doNavigate, setDoNavigate] = useState(false);
|
||||
const [playNextUrl, setPlayNextUrl] = useState(true);
|
||||
const [countdownCanceled, setCountdownCanceled] = useState(false);
|
||||
|
||||
const isCurrentClaimLive = activeLivestreamForChannel && activeLivestreamForChannel.claimId === claimId;
|
||||
const isFree = costInfo && costInfo.cost === 0;
|
||||
const canViewFile = isFree || claimWasPurchased;
|
||||
const isPlayable = RENDER_MODES.FLOATING_MODES.includes(renderMode) || activeLivestreamForChannel;
|
||||
const isReadyToPlay = isPlayable && streamingUrl;
|
||||
|
||||
const handleResize = React.useCallback(() => {
|
||||
const element = document.querySelector(`.${PRIMARY_PLAYER_WRAPPER_CLASS}`);
|
||||
|
||||
if (!element) return;
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
|
||||
// getBoundingClientRect returns a DomRect, not an object
|
||||
const objectRect = {
|
||||
top: rect.top,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
left: rect.left,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
// $FlowFixMe
|
||||
x: rect.x,
|
||||
};
|
||||
|
||||
// $FlowFixMe
|
||||
setFileViewerRect({ ...objectRect });
|
||||
}, []);
|
||||
|
||||
// Initial resize, will place the player correctly above the cover when starts playing
|
||||
// (remember the URI here is from playingUri). The cover then keeps on the page and kind of serves as a placeholder
|
||||
// for the player size and gives the content layered behind the player a "max scroll height"
|
||||
useEffect(() => {
|
||||
if (uri) {
|
||||
handleResize();
|
||||
setCountdownCanceled(false);
|
||||
}
|
||||
}, [handleResize, uri]);
|
||||
|
||||
useEffect(() => {
|
||||
handleResize();
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
onFullscreenChange(window, 'add', handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
onFullscreenChange(window, 'remove', handleResize);
|
||||
};
|
||||
}, [handleResize]);
|
||||
|
||||
const doPlay = React.useCallback(
|
||||
(playUri) => {
|
||||
setDoNavigate(false);
|
||||
const navigateUrl = formatLbryUrlForWeb(playUri);
|
||||
push({
|
||||
pathname: navigateUrl,
|
||||
search: collectionId && generateListSearchUrlParams(collectionId),
|
||||
state: { collectionId, forceAutoplay: true, hideFloatingPlayer: true },
|
||||
});
|
||||
},
|
||||
[collectionId, push]
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!doNavigate) return;
|
||||
|
||||
if (playNextUrl && nextListUri) {
|
||||
doPlay(nextListUri);
|
||||
} else if (previousListUri) {
|
||||
doPlay(previousListUri);
|
||||
}
|
||||
setPlayNextUrl(true);
|
||||
}, [doNavigate, doPlay, nextListUri, playNextUrl, previousListUri]);
|
||||
|
||||
if (!isPlayable || !uri || countdownCanceled || (collectionId && !canViewFile && !nextListUri)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="content__viewer content__viewer--inline content__viewer--mobile"
|
||||
style={
|
||||
fileViewerRect
|
||||
? {
|
||||
width: fileViewerRect.width,
|
||||
height: fileViewerRect.height,
|
||||
left: fileViewerRect.x,
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<div className="content__wrapper">
|
||||
<React.Suspense fallback={<Loading />}>
|
||||
{isCurrentClaimLive && channelClaimId ? (
|
||||
<LivestreamIframeRender channelClaimId={channelClaimId} showLivestream mobileVersion />
|
||||
) : isReadyToPlay ? (
|
||||
<FileRender uri={uri} />
|
||||
) : !canViewFile ? (
|
||||
<div className="content__loading">
|
||||
<AutoplayCountdown
|
||||
nextRecommendedUri={nextListUri}
|
||||
doNavigate={() => setDoNavigate(true)}
|
||||
doReplay={() => doPlayUri(uri)}
|
||||
doPrevious={() => {
|
||||
setPlayNextUrl(false);
|
||||
setDoNavigate(true);
|
||||
}}
|
||||
onCanceled={() => setCountdownCanceled(true)}
|
||||
skipPaid
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Loading />
|
||||
)}
|
||||
</React.Suspense>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const Loading = () => <LoadingScreen status={__('Loading')} />;
|
39
ui/component/livestreamLayout/iframe-render.jsx
Normal file
39
ui/component/livestreamLayout/iframe-render.jsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
// @flow
|
||||
import { LIVESTREAM_EMBED_URL } from 'constants/livestream';
|
||||
import LivestreamScheduledInfo from 'component/livestreamScheduledInfo';
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
type Props = {
|
||||
channelClaimId: string,
|
||||
release?: any,
|
||||
showLivestream: boolean,
|
||||
showScheduledInfo?: boolean,
|
||||
mobileVersion?: boolean,
|
||||
};
|
||||
|
||||
export default function LivestreamIframeRender(props: Props) {
|
||||
const { channelClaimId, release, showLivestream, showScheduledInfo, mobileVersion } = props;
|
||||
|
||||
const className = mobileVersion
|
||||
? 'file-render file-render--video'
|
||||
: classnames('file-render file-render--video livestream', {
|
||||
'file-render--scheduledLivestream': !showLivestream,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className="file-viewer">
|
||||
{showLivestream && (
|
||||
<iframe
|
||||
src={`${LIVESTREAM_EMBED_URL}/${channelClaimId}?skin=odysee&autoplay=1`}
|
||||
scrolling="no"
|
||||
allowFullScreen
|
||||
/>
|
||||
)}
|
||||
|
||||
{showScheduledInfo && release && <LivestreamScheduledInfo release={release} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
// @flow
|
||||
import { lazyImport } from 'util/lazyImport';
|
||||
import { LIVESTREAM_EMBED_URL } from 'constants/livestream';
|
||||
import { useIsMobile } from 'effects/use-screensize';
|
||||
import classnames from 'classnames';
|
||||
import FileTitleSection from 'component/fileTitleSection';
|
||||
import LivestreamLink from 'component/livestreamLink';
|
||||
import LivestreamScheduledInfo from 'component/livestreamScheduledInfo';
|
||||
import React from 'react';
|
||||
import { PRIMARY_PLAYER_WRAPPER_CLASS } from 'page/file/view';
|
||||
import FileRenderInitiator from 'component/fileRenderInitiator';
|
||||
import LivestreamIframeRender from './iframe-render';
|
||||
|
||||
const LivestreamChatLayout = lazyImport(() => import('component/livestreamChatLayout' /* webpackChunkName: "chat" */));
|
||||
|
||||
|
@ -42,23 +42,21 @@ export default function LivestreamLayout(props: Props) {
|
|||
return (
|
||||
<>
|
||||
<div className="section card-stack">
|
||||
<div
|
||||
className={classnames('file-render file-render--video livestream', {
|
||||
'file-render--scheduledLivestream': !showLivestream,
|
||||
})}
|
||||
>
|
||||
<div className="file-viewer">
|
||||
{showLivestream && (
|
||||
<iframe
|
||||
src={`${LIVESTREAM_EMBED_URL}/${channelClaimId}?skin=odysee&autoplay=1`}
|
||||
scrolling="no"
|
||||
allowFullScreen
|
||||
/>
|
||||
)}
|
||||
|
||||
{showScheduledInfo && <LivestreamScheduledInfo release={release} />}
|
||||
</div>
|
||||
</div>
|
||||
<React.Suspense fallback={null}>
|
||||
{isMobile && isCurrentClaimLive ? (
|
||||
<div className={PRIMARY_PLAYER_WRAPPER_CLASS}>
|
||||
{/* Mobile needs to handle the livestream player like any video player */}
|
||||
<FileRenderInitiator uri={uri} />
|
||||
</div>
|
||||
) : (
|
||||
<LivestreamIframeRender
|
||||
channelClaimId={channelClaimId}
|
||||
release={release}
|
||||
showLivestream={showLivestream}
|
||||
showScheduledInfo={showScheduledInfo}
|
||||
/>
|
||||
)}
|
||||
</React.Suspense>
|
||||
|
||||
{hideComments && !showScheduledInfo && (
|
||||
<div className="help--notice">
|
||||
|
|
|
@ -4,6 +4,12 @@
|
|||
top: var(--spacing-s);
|
||||
}
|
||||
|
||||
.content__viewer--mobile {
|
||||
border-radius: 0;
|
||||
position: fixed;
|
||||
top: var(--header-height-mobile);
|
||||
}
|
||||
|
||||
.content__viewer--disable-click {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
@ -11,6 +17,10 @@
|
|||
.content__viewer--inline {
|
||||
max-height: var(--inline-player-max-height);
|
||||
border: none;
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
max-height: var(--mobile-player-max-height);
|
||||
}
|
||||
}
|
||||
|
||||
.content__viewer--secondary {
|
||||
|
@ -127,6 +137,13 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
margin: 0;
|
||||
max-height: var(--mobile-player-max-height);
|
||||
}
|
||||
}
|
||||
|
||||
.content__cover--text {
|
||||
|
|
|
@ -24,6 +24,10 @@ $recent-msg-button__height: 2rem;
|
|||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
margin: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.livestream__chat--popout {
|
||||
|
|
|
@ -217,6 +217,19 @@
|
|||
padding: var(--spacing-xs);
|
||||
flex-direction: column;
|
||||
padding-top: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
.file-page__secondary-content {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 0;
|
||||
margin-bottom: var(--spacing-xxs) !important;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -297,7 +310,19 @@
|
|||
}
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-medium) {
|
||||
@media (max-width: $breakpoint-small) {
|
||||
padding: 0;
|
||||
|
||||
.card {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.card-stack {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $breakpoint-small) and (max-width: $breakpoint-medium) {
|
||||
padding: 0 var(--spacing-m);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
|
||||
// Inline Player
|
||||
--inline-player-max-height: calc(100vh - var(--header-height) - var(--spacing-l) * 4);
|
||||
--mobile-player-max-height: 50vh;
|
||||
|
||||
// Card
|
||||
--card-radius: var(--border-radius);
|
||||
|
|
Loading…
Reference in a new issue