Create swipeableDrawer component
- CommentsList needs to return a title with comment amounts - mobile player dimensions needed to fill in the cover - hid livestream header for now until figuring out a better presentation - ~colum-reverse~ was causing problems with MUI's drawer scrolling, so reversed the chat order and made it ~column~ by default - Hid bottom expand navigation if component not yet opened - some other style changes in the middle
This commit is contained in:
parent
ce11a4b9c1
commit
baf51f9c32
21 changed files with 432 additions and 96 deletions
|
@ -4,6 +4,7 @@
|
||||||
.*/node_modules/react-plastic/.*
|
.*/node_modules/react-plastic/.*
|
||||||
.*/node_modules/raf-schd/.*
|
.*/node_modules/raf-schd/.*
|
||||||
.*/node_modules/react-beautiful-dnd/.*
|
.*/node_modules/react-beautiful-dnd/.*
|
||||||
|
.*/node_modules/@emotion/.*
|
||||||
|
|
||||||
[include]
|
[include]
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@ type Props = {
|
||||||
commentsAreExpanded?: boolean,
|
commentsAreExpanded?: boolean,
|
||||||
fetchReacts: (Array<string>) => Promise<any>,
|
fetchReacts: (Array<string>) => Promise<any>,
|
||||||
doResolveUris: (Array<string>) => void,
|
doResolveUris: (Array<string>) => void,
|
||||||
|
setCommentListTitle?: (string) => void,
|
||||||
fetchTopLevelComments: (string, number, number, number) => void,
|
fetchTopLevelComments: (string, number, number, number) => void,
|
||||||
fetchComment: (string) => void,
|
fetchComment: (string) => void,
|
||||||
resetComments: (string) => void,
|
resetComments: (string) => void,
|
||||||
|
@ -77,6 +78,7 @@ function CommentList(props: Props) {
|
||||||
commentsAreExpanded,
|
commentsAreExpanded,
|
||||||
fetchReacts,
|
fetchReacts,
|
||||||
doResolveUris,
|
doResolveUris,
|
||||||
|
setCommentListTitle,
|
||||||
fetchTopLevelComments,
|
fetchTopLevelComments,
|
||||||
fetchComment,
|
fetchComment,
|
||||||
resetComments,
|
resetComments,
|
||||||
|
@ -84,13 +86,13 @@ function CommentList(props: Props) {
|
||||||
|
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const isMediumScreen = useIsMediumScreen();
|
const isMediumScreen = useIsMediumScreen();
|
||||||
const desktopView = !isMobile && !isMediumScreen;
|
|
||||||
const spinnerRef = React.useRef();
|
const spinnerRef = React.useRef();
|
||||||
const DEFAULT_SORT = ENABLE_COMMENT_REACTIONS ? SORT_BY.POPULARITY : SORT_BY.NEWEST;
|
const DEFAULT_SORT = ENABLE_COMMENT_REACTIONS ? SORT_BY.POPULARITY : SORT_BY.NEWEST;
|
||||||
const [sort, setSort] = usePersistedState('comment-sort-by', DEFAULT_SORT);
|
const [sort, setSort] = usePersistedState('comment-sort-by', DEFAULT_SORT);
|
||||||
const [page, setPage] = React.useState(0);
|
const [page, setPage] = React.useState(0);
|
||||||
const [commentsToDisplay, setCommentsToDisplay] = React.useState(topLevelComments);
|
const [commentsToDisplay, setCommentsToDisplay] = React.useState(topLevelComments);
|
||||||
const hasDefaultExpansion = commentsAreExpanded || desktopView;
|
const hasDefaultExpansion = commentsAreExpanded || !isMediumScreen || isMobile;
|
||||||
const [expandedComments, setExpandedComments] = React.useState(hasDefaultExpansion);
|
const [expandedComments, setExpandedComments] = React.useState(hasDefaultExpansion);
|
||||||
const totalFetchedComments = allCommentIds ? allCommentIds.length : 0;
|
const totalFetchedComments = allCommentIds ? allCommentIds.length : 0;
|
||||||
const channelId = getChannelIdFromClaim(claim);
|
const channelId = getChannelIdFromClaim(claim);
|
||||||
|
@ -99,6 +101,11 @@ function CommentList(props: Props) {
|
||||||
const isResolvingComments = topLevelComments && resolvedComments.length !== topLevelComments.length;
|
const isResolvingComments = topLevelComments && resolvedComments.length !== topLevelComments.length;
|
||||||
const alreadyResolved = !isResolvingComments && resolvedComments.length !== 0;
|
const alreadyResolved = !isResolvingComments && resolvedComments.length !== 0;
|
||||||
const canDisplayComments = commentsToDisplay && commentsToDisplay.length === topLevelComments.length;
|
const canDisplayComments = commentsToDisplay && commentsToDisplay.length === topLevelComments.length;
|
||||||
|
const title =
|
||||||
|
(totalComments === 0 && __('Leave a comment')) ||
|
||||||
|
(totalComments === 1 && __('1 comment')) ||
|
||||||
|
__('%total_comments% comments', { total_comments: totalComments });
|
||||||
|
if (setCommentListTitle) setCommentListTitle(title);
|
||||||
|
|
||||||
// Display comments immediately if not fetching reactions
|
// Display comments immediately if not fetching reactions
|
||||||
// If not, wait to show comments until reactions are fetched
|
// If not, wait to show comments until reactions are fetched
|
||||||
|
@ -283,11 +290,7 @@ function CommentList(props: Props) {
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
className="card--enable-overflow"
|
className="card--enable-overflow"
|
||||||
title={
|
title={!isMobile && title}
|
||||||
(totalComments === 0 && __('Leave a comment')) ||
|
|
||||||
(totalComments === 1 && __('1 comment')) ||
|
|
||||||
__('%total_comments% comments', { total_comments: totalComments })
|
|
||||||
}
|
|
||||||
titleActions={
|
titleActions={
|
||||||
<>
|
<>
|
||||||
{totalComments > 1 && ENABLE_COMMENT_REACTIONS && (
|
{totalComments > 1 && ENABLE_COMMENT_REACTIONS && (
|
||||||
|
@ -310,8 +313,8 @@ function CommentList(props: Props) {
|
||||||
|
|
||||||
<ul
|
<ul
|
||||||
className={classnames({
|
className={classnames({
|
||||||
comments: desktopView || expandedComments,
|
comments: isMediumScreen || expandedComments,
|
||||||
'comments--contracted': !desktopView && !expandedComments,
|
'comments--contracted': !isMediumScreen && !expandedComments,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{readyToDisplayComments && pinnedComments && getCommentElems(pinnedComments)}
|
{readyToDisplayComments && pinnedComments && getCommentElems(pinnedComments)}
|
||||||
|
|
|
@ -8,10 +8,12 @@ import {
|
||||||
import { selectPlayingUri, makeSelectFileRenderModeForUri } from 'redux/selectors/content';
|
import { selectPlayingUri, makeSelectFileRenderModeForUri } from 'redux/selectors/content';
|
||||||
import { selectCostInfoForUri } from 'lbryinc';
|
import { selectCostInfoForUri } from 'lbryinc';
|
||||||
import { doPlayUri } from 'redux/actions/content';
|
import { doPlayUri } from 'redux/actions/content';
|
||||||
|
import { doSetMobilePlayerDimensions } from 'redux/actions/app';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { getChannelIdFromClaim } from 'util/claim';
|
import { getChannelIdFromClaim } from 'util/claim';
|
||||||
import { selectActiveLivestreamForChannel } from 'redux/selectors/livestream';
|
import { selectActiveLivestreamForChannel } from 'redux/selectors/livestream';
|
||||||
import FileRenderMobile from './view';
|
import FileRenderMobile from './view';
|
||||||
|
import { selectMobilePlayerDimensions } from 'redux/selectors/app';
|
||||||
|
|
||||||
const select = (state, props) => {
|
const select = (state, props) => {
|
||||||
const playingUri = selectPlayingUri(state);
|
const playingUri = selectPlayingUri(state);
|
||||||
|
@ -34,11 +36,14 @@ const select = (state, props) => {
|
||||||
activeLivestreamForChannel: channelClaimId && selectActiveLivestreamForChannel(state, channelClaimId),
|
activeLivestreamForChannel: channelClaimId && selectActiveLivestreamForChannel(state, channelClaimId),
|
||||||
claimId,
|
claimId,
|
||||||
channelClaimId,
|
channelClaimId,
|
||||||
|
mobilePlayerDimensions: selectMobilePlayerDimensions(state),
|
||||||
|
playingUri,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const perform = {
|
const perform = {
|
||||||
doPlayUri,
|
doPlayUri,
|
||||||
|
doSetMobilePlayerDimensions,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRouter(connect(select, perform)(FileRenderMobile));
|
export default withRouter(connect(select, perform)(FileRenderMobile));
|
||||||
|
|
|
@ -11,6 +11,7 @@ import LivestreamIframeRender from 'component/livestreamLayout/iframe-render';
|
||||||
|
|
||||||
const PRIMARY_PLAYER_WRAPPER_CLASS = 'file-page__video-container';
|
const PRIMARY_PLAYER_WRAPPER_CLASS = 'file-page__video-container';
|
||||||
export const INLINE_PLAYER_WRAPPER_CLASS = 'inline-player__wrapper';
|
export const INLINE_PLAYER_WRAPPER_CLASS = 'inline-player__wrapper';
|
||||||
|
export const HEADER_HEIGHT_MOBILE = 56;
|
||||||
|
|
||||||
// ****************************************************************************
|
// ****************************************************************************
|
||||||
// ****************************************************************************
|
// ****************************************************************************
|
||||||
|
@ -27,7 +28,10 @@ type Props = {
|
||||||
previousListUri: string,
|
previousListUri: string,
|
||||||
activeLivestreamForChannel?: any,
|
activeLivestreamForChannel?: any,
|
||||||
channelClaimId?: any,
|
channelClaimId?: any,
|
||||||
|
playingUri?: PlayingUri,
|
||||||
|
mobilePlayerDimensions?: any,
|
||||||
doPlayUri: (string) => void,
|
doPlayUri: (string) => void,
|
||||||
|
doSetMobilePlayerDimensions: (height: number, width: number) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function FileRenderMobile(props: Props) {
|
export default function FileRenderMobile(props: Props) {
|
||||||
|
@ -43,7 +47,10 @@ export default function FileRenderMobile(props: Props) {
|
||||||
previousListUri,
|
previousListUri,
|
||||||
activeLivestreamForChannel,
|
activeLivestreamForChannel,
|
||||||
channelClaimId,
|
channelClaimId,
|
||||||
|
playingUri,
|
||||||
|
mobilePlayerDimensions,
|
||||||
doPlayUri,
|
doPlayUri,
|
||||||
|
doSetMobilePlayerDimensions,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
|
@ -58,6 +65,7 @@ export default function FileRenderMobile(props: Props) {
|
||||||
const canViewFile = isFree || claimWasPurchased;
|
const canViewFile = isFree || claimWasPurchased;
|
||||||
const isPlayable = RENDER_MODES.FLOATING_MODES.includes(renderMode) || activeLivestreamForChannel;
|
const isPlayable = RENDER_MODES.FLOATING_MODES.includes(renderMode) || activeLivestreamForChannel;
|
||||||
const isReadyToPlay = isPlayable && streamingUrl;
|
const isReadyToPlay = isPlayable && streamingUrl;
|
||||||
|
const isCurrentMediaPlaying = playingUri && playingUri.uri === uri;
|
||||||
|
|
||||||
const handleResize = React.useCallback(() => {
|
const handleResize = React.useCallback(() => {
|
||||||
const element = document.querySelector(`.${PRIMARY_PLAYER_WRAPPER_CLASS}`);
|
const element = document.querySelector(`.${PRIMARY_PLAYER_WRAPPER_CLASS}`);
|
||||||
|
@ -80,7 +88,11 @@ export default function FileRenderMobile(props: Props) {
|
||||||
|
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
setFileViewerRect({ ...objectRect });
|
setFileViewerRect({ ...objectRect });
|
||||||
}, []);
|
|
||||||
|
if (doSetMobilePlayerDimensions && (!mobilePlayerDimensions || mobilePlayerDimensions.height !== rect.height)) {
|
||||||
|
doSetMobilePlayerDimensions(rect.height, rect.width);
|
||||||
|
}
|
||||||
|
}, [doSetMobilePlayerDimensions, mobilePlayerDimensions]);
|
||||||
|
|
||||||
// Initial resize, will place the player correctly above the cover when starts playing
|
// 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
|
// (remember the URI here is from playingUri). The cover then keeps on the page and kind of serves as a placeholder
|
||||||
|
@ -128,7 +140,13 @@ export default function FileRenderMobile(props: Props) {
|
||||||
setPlayNextUrl(true);
|
setPlayNextUrl(true);
|
||||||
}, [doNavigate, doPlay, nextListUri, playNextUrl, previousListUri]);
|
}, [doNavigate, doPlay, nextListUri, playNextUrl, previousListUri]);
|
||||||
|
|
||||||
if (!isPlayable || !uri || countdownCanceled || (collectionId && !canViewFile && !nextListUri)) {
|
if (
|
||||||
|
!isCurrentMediaPlaying ||
|
||||||
|
!isPlayable ||
|
||||||
|
!uri ||
|
||||||
|
countdownCanceled ||
|
||||||
|
(collectionId && !canViewFile && !nextListUri)
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,32 +164,28 @@ export default function FileRenderMobile(props: Props) {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="content__wrapper">
|
<div className="content__wrapper">
|
||||||
<React.Suspense fallback={<Loading />}>
|
{isCurrentClaimLive && channelClaimId ? (
|
||||||
{isCurrentClaimLive && channelClaimId ? (
|
<LivestreamIframeRender channelClaimId={channelClaimId} showLivestream mobileVersion />
|
||||||
<LivestreamIframeRender channelClaimId={channelClaimId} showLivestream mobileVersion />
|
) : isReadyToPlay ? (
|
||||||
) : isReadyToPlay ? (
|
<FileRender uri={uri} />
|
||||||
<FileRender uri={uri} />
|
) : !canViewFile ? (
|
||||||
) : !canViewFile ? (
|
<div className="content__loading">
|
||||||
<div className="content__loading">
|
<AutoplayCountdown
|
||||||
<AutoplayCountdown
|
nextRecommendedUri={nextListUri}
|
||||||
nextRecommendedUri={nextListUri}
|
doNavigate={() => setDoNavigate(true)}
|
||||||
doNavigate={() => setDoNavigate(true)}
|
doReplay={() => doPlayUri(uri)}
|
||||||
doReplay={() => doPlayUri(uri)}
|
doPrevious={() => {
|
||||||
doPrevious={() => {
|
setPlayNextUrl(false);
|
||||||
setPlayNextUrl(false);
|
setDoNavigate(true);
|
||||||
setDoNavigate(true);
|
}}
|
||||||
}}
|
onCanceled={() => setCountdownCanceled(true)}
|
||||||
onCanceled={() => setCountdownCanceled(true)}
|
skipPaid
|
||||||
skipPaid
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
) : (
|
<LoadingScreen status={__('Loading')} />
|
||||||
<Loading />
|
)}
|
||||||
)}
|
|
||||||
</React.Suspense>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Loading = () => <LoadingScreen status={__('Loading')} />;
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ type Props = {
|
||||||
pinnedComments: Array<Comment>,
|
pinnedComments: Array<Comment>,
|
||||||
superChats: Array<Comment>,
|
superChats: Array<Comment>,
|
||||||
uri: string,
|
uri: string,
|
||||||
|
hideHeader?: boolean,
|
||||||
doCommentList: (string, string, number, number) => void,
|
doCommentList: (string, string, number, number) => void,
|
||||||
doResolveUris: (Array<string>, boolean) => void,
|
doResolveUris: (Array<string>, boolean) => void,
|
||||||
doSuperChatList: (string) => void,
|
doSuperChatList: (string) => void,
|
||||||
|
@ -55,6 +56,7 @@ export default function LivestreamChatLayout(props: Props) {
|
||||||
pinnedComments,
|
pinnedComments,
|
||||||
superChats: superChatsByAmount,
|
superChats: superChatsByAmount,
|
||||||
uri,
|
uri,
|
||||||
|
hideHeader,
|
||||||
doCommentList,
|
doCommentList,
|
||||||
doResolveUris,
|
doResolveUris,
|
||||||
doSuperChatList,
|
doSuperChatList,
|
||||||
|
@ -242,60 +244,62 @@ export default function LivestreamChatLayout(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classnames('card livestream__chat', { 'livestream__chat--popout': isPopoutWindow })}>
|
<div className={classnames('card livestream__chat', { 'livestream__chat--popout': isPopoutWindow })}>
|
||||||
<div className="card__header--between livestreamDiscussion__header">
|
{!hideHeader && (
|
||||||
<div className="card__title-section--small livestreamDiscussion__title">
|
<div className="card__header--between livestreamDiscussion__header">
|
||||||
{__('Live Chat')}
|
<div className="card__title-section--small livestreamDiscussion__title">
|
||||||
|
{__('Live Chat')}
|
||||||
|
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuButton className="menu__button">
|
<MenuButton className="menu__button">
|
||||||
<Icon size={18} icon={ICONS.SETTINGS} />
|
<Icon size={18} icon={ICONS.SETTINGS} />
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
|
|
||||||
<MenuList className="menu__list">
|
<MenuList className="menu__list">
|
||||||
<MenuItem className="comment__menu-option" onSelect={TOGGLE_TIMESTAMP_OPACITY}>
|
<MenuItem className="comment__menu-option" onSelect={TOGGLE_TIMESTAMP_OPACITY}>
|
||||||
<span className="menu__link">
|
<span className="menu__link">
|
||||||
<Icon aria-hidden icon={ICONS.TIME} />
|
<Icon aria-hidden icon={ICONS.TIME} />
|
||||||
{__('Toggle Timestamps')}
|
{__('Toggle Timestamps')}
|
||||||
</span>
|
</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<MenuItem className="comment__menu-option" onSelect={() => setChatHidden(true)}>
|
<MenuItem className="comment__menu-option" onSelect={() => setChatHidden(true)}>
|
||||||
<span className="menu__link">
|
<span className="menu__link">
|
||||||
<Icon aria-hidden icon={ICONS.EYE} />
|
<Icon aria-hidden icon={ICONS.EYE} />
|
||||||
{__('Hide Chat')}
|
{__('Hide Chat')}
|
||||||
</span>
|
</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
{!isPopoutWindow && !isMobile && (
|
{!isPopoutWindow && !isMobile && (
|
||||||
|
<>
|
||||||
|
<MenuItem className="comment__menu-option" onSelect={handlePopout}>
|
||||||
|
<span className="menu__link">
|
||||||
|
<Icon aria-hidden icon={ICONS.EXTERNAL} />
|
||||||
|
{__('Popout Chat')}
|
||||||
|
</span>
|
||||||
|
</MenuItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</MenuList>
|
||||||
|
</Menu>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{superChatsByAmount && (
|
||||||
|
<div className="recommended-content__toggles">
|
||||||
|
{/* the superchats in chronological order button */}
|
||||||
|
{chatContentToggle(VIEW_MODES.CHAT, __('Chat'))}
|
||||||
|
|
||||||
|
{/* the button to show superchats listed by most to least support amount */}
|
||||||
|
{chatContentToggle(
|
||||||
|
VIEW_MODES.SUPERCHAT,
|
||||||
<>
|
<>
|
||||||
<MenuItem className="comment__menu-option" onSelect={handlePopout}>
|
<CreditAmount amount={superChatsLBCAmount || 0} size={8} /> /
|
||||||
<span className="menu__link">
|
<CreditAmount amount={superChatsFiatAmount || 0} size={8} isFiat /> {__('Tipped')}
|
||||||
<Icon aria-hidden icon={ICONS.EXTERNAL} />
|
|
||||||
{__('Popout Chat')}
|
|
||||||
</span>
|
|
||||||
</MenuItem>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</MenuList>
|
</div>
|
||||||
</Menu>
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
{superChatsByAmount && (
|
|
||||||
<div className="recommended-content__toggles">
|
|
||||||
{/* the superchats in chronological order button */}
|
|
||||||
{chatContentToggle(VIEW_MODES.CHAT, __('Chat'))}
|
|
||||||
|
|
||||||
{/* the button to show superchats listed by most to least support amount */}
|
|
||||||
{chatContentToggle(
|
|
||||||
VIEW_MODES.SUPERCHAT,
|
|
||||||
<>
|
|
||||||
<CreditAmount amount={superChatsLBCAmount || 0} size={8} /> /
|
|
||||||
<CreditAmount amount={superChatsFiatAmount || 0} size={8} isFiat /> {__('Tipped')}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div ref={commentsRef} className="livestreamComments__wrapper">
|
<div ref={commentsRef} className="livestreamComments__wrapper">
|
||||||
{viewMode === VIEW_MODES.CHAT && superChatsByAmount && (
|
{viewMode === VIEW_MODES.CHAT && superChatsByAmount && (
|
||||||
|
|
|
@ -45,9 +45,12 @@ export default function LivestreamComments(props: Props) {
|
||||||
if (!fetchingComments && commentsToDisplay && commentsToDisplay.length > 0) {
|
if (!fetchingComments && commentsToDisplay && commentsToDisplay.length > 0) {
|
||||||
return (
|
return (
|
||||||
<div className="livestream__comments">
|
<div className="livestream__comments">
|
||||||
{commentsToDisplay.map((comment) => (
|
{commentsToDisplay
|
||||||
<LivestreamComment comment={comment} key={comment.comment_id} uri={uri} forceUpdate={forceUpdate} />
|
.slice(0)
|
||||||
))}
|
.reverse()
|
||||||
|
.map((comment) => (
|
||||||
|
<LivestreamComment comment={comment} key={comment.comment_id} uri={uri} forceUpdate={forceUpdate} />
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,9 @@ import React from 'react';
|
||||||
import { PRIMARY_PLAYER_WRAPPER_CLASS } from 'page/file/view';
|
import { PRIMARY_PLAYER_WRAPPER_CLASS } from 'page/file/view';
|
||||||
import FileRenderInitiator from 'component/fileRenderInitiator';
|
import FileRenderInitiator from 'component/fileRenderInitiator';
|
||||||
import LivestreamIframeRender from './iframe-render';
|
import LivestreamIframeRender from './iframe-render';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import * as ICONS from 'constants/icons';
|
||||||
|
import SwipeableDrawer from 'component/swipeableDrawer';
|
||||||
|
|
||||||
const LivestreamChatLayout = lazyImport(() => import('component/livestreamChatLayout' /* webpackChunkName: "chat" */));
|
const LivestreamChatLayout = lazyImport(() => import('component/livestreamChatLayout' /* webpackChunkName: "chat" */));
|
||||||
|
|
||||||
|
@ -35,6 +38,9 @@ export default function LivestreamLayout(props: Props) {
|
||||||
|
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
|
const [showChat, setShowChat] = React.useState(undefined);
|
||||||
|
const drawerWasToggled = showChat !== undefined;
|
||||||
|
|
||||||
if (!claim || !claim.signing_channel) return null;
|
if (!claim || !claim.signing_channel) return null;
|
||||||
|
|
||||||
const { name: channelName, claim_id: channelClaimId } = claim.signing_channel;
|
const { name: channelName, claim_id: channelClaimId } = claim.signing_channel;
|
||||||
|
@ -85,10 +91,27 @@ export default function LivestreamLayout(props: Props) {
|
||||||
|
|
||||||
{isMobile && !hideComments && (
|
{isMobile && !hideComments && (
|
||||||
<React.Suspense fallback={null}>
|
<React.Suspense fallback={null}>
|
||||||
<LivestreamChatLayout uri={uri} />
|
<SwipeableDrawer
|
||||||
|
open={Boolean(showChat)}
|
||||||
|
toggleDrawer={() => setShowChat(!showChat)}
|
||||||
|
title={__('Live Chat')}
|
||||||
|
didInitialDisplay={drawerWasToggled}
|
||||||
|
>
|
||||||
|
<LivestreamChatLayout uri={uri} hideHeader />
|
||||||
|
</SwipeableDrawer>
|
||||||
</React.Suspense>
|
</React.Suspense>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isMobile && (
|
||||||
|
<Button
|
||||||
|
className="swipeable-drawer__expand-button"
|
||||||
|
label="Open Live Chat"
|
||||||
|
button="primary"
|
||||||
|
icon={ICONS.CHAT}
|
||||||
|
onClick={() => setShowChat(!showChat)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<FileTitleSection uri={uri} livestream isLive={showLivestream} />
|
<FileTitleSection uri={uri} livestream isLive={showLivestream} />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
11
ui/component/swipeableDrawer/index.js
Normal file
11
ui/component/swipeableDrawer/index.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import LivestreamLayout from './view';
|
||||||
|
import { selectTheme } from 'redux/selectors/settings';
|
||||||
|
import { selectMobilePlayerDimensions } from 'redux/selectors/app';
|
||||||
|
|
||||||
|
const select = (state) => ({
|
||||||
|
theme: selectTheme(state),
|
||||||
|
mobilePlayerDimensions: selectMobilePlayerDimensions(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select)(LivestreamLayout);
|
109
ui/component/swipeableDrawer/view.jsx
Normal file
109
ui/component/swipeableDrawer/view.jsx
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
// @flow
|
||||||
|
// $FlowFixMe
|
||||||
|
import { Global } from '@emotion/react';
|
||||||
|
// $FlowFixMe
|
||||||
|
import { grey } from '@mui/material/colors';
|
||||||
|
// $FlowFixMe
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import { HEADER_HEIGHT_MOBILE } from 'component/fileRenderMobile/view';
|
||||||
|
import { SwipeableDrawer as MUIDrawer } from '@mui/material';
|
||||||
|
import * as ICONS from 'constants/icons';
|
||||||
|
import * as React from 'react';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import Icon from 'component/common/icon';
|
||||||
|
import Portal from '@mui/material/Portal';
|
||||||
|
|
||||||
|
const DRAWER_PULLER_HEIGHT = 42;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: Node,
|
||||||
|
open: Boolean,
|
||||||
|
theme: string,
|
||||||
|
mobilePlayerDimensions?: { height: number },
|
||||||
|
title: string,
|
||||||
|
didInitialDisplay?: boolean,
|
||||||
|
toggleDrawer: () => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function SwipeableDrawer(props: Props) {
|
||||||
|
const { mobilePlayerDimensions, title, children, open, theme, didInitialDisplay, toggleDrawer } = props;
|
||||||
|
|
||||||
|
const [coverHeight, setCoverHeight] = React.useState();
|
||||||
|
|
||||||
|
const videoHeight = coverHeight || (mobilePlayerDimensions ? mobilePlayerDimensions.height : 0);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (open && !mobilePlayerDimensions) {
|
||||||
|
const element = document.querySelector(`.file-page__video-container`);
|
||||||
|
|
||||||
|
if (element) {
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
setCoverHeight(rect.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [mobilePlayerDimensions, open]);
|
||||||
|
|
||||||
|
const drawerGlobalStyles = (
|
||||||
|
<Global
|
||||||
|
styles={{
|
||||||
|
'.main-wrapper__inner--filepage': {
|
||||||
|
'padding-bottom': didInitialDisplay ? `${DRAWER_PULLER_HEIGHT}px !important` : 'inherit',
|
||||||
|
overflow: open ? 'hidden' : 'unset',
|
||||||
|
'max-height': open ? '100vh' : 'unset',
|
||||||
|
},
|
||||||
|
'.MuiDrawer-root': {
|
||||||
|
top: `calc(${HEADER_HEIGHT_MOBILE}px + ${videoHeight}px) !important`,
|
||||||
|
},
|
||||||
|
'.MuiDrawer-root > .MuiPaper-root': {
|
||||||
|
overflow: 'visible',
|
||||||
|
color: 'var(--color-text)',
|
||||||
|
position: 'absolute',
|
||||||
|
height: `calc(100% - ${DRAWER_PULLER_HEIGHT}px)`,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Puller = () => (
|
||||||
|
<span className="swipeable-drawer__puller" style={{ backgroundColor: theme === 'light' ? grey[300] : grey[800] }} />
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{drawerGlobalStyles}
|
||||||
|
|
||||||
|
<MUIDrawer
|
||||||
|
anchor="bottom"
|
||||||
|
open={open}
|
||||||
|
onClose={toggleDrawer}
|
||||||
|
onOpen={toggleDrawer}
|
||||||
|
hideBackdrop
|
||||||
|
disableEnforceFocus
|
||||||
|
disablePortal
|
||||||
|
disableSwipeToOpen
|
||||||
|
ModalProps={{ keepMounted: true }}
|
||||||
|
>
|
||||||
|
{didInitialDisplay && (
|
||||||
|
<div className="swipeable-drawer__header" style={{ top: -DRAWER_PULLER_HEIGHT }}>
|
||||||
|
{open ? (
|
||||||
|
<>
|
||||||
|
<Puller />
|
||||||
|
<Typography sx={{ p: 1.5, color: 'var(--color-text)', display: 'flex' }}>{title}</Typography>
|
||||||
|
<Button button="close" icon={ICONS.REMOVE} onClick={toggleDrawer} />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Portal>
|
||||||
|
<div className="swipeable-drawer__expand" onClick={toggleDrawer}>
|
||||||
|
<Typography sx={{ p: 1.5, color: 'var(--color-text)' }}>{title}</Typography>
|
||||||
|
<Icon icon={ICONS.UP} />
|
||||||
|
</div>
|
||||||
|
</Portal>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{children}
|
||||||
|
</MUIDrawer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ export const TOGGLE_YOUTUBE_SYNC_INTEREST = 'TOGGLE_YOUTUBE_SYNC_INTEREST';
|
||||||
export const TOGGLE_SPLASH_ANIMATION = 'TOGGLE_SPLASH_ANIMATION';
|
export const TOGGLE_SPLASH_ANIMATION = 'TOGGLE_SPLASH_ANIMATION';
|
||||||
export const SET_ACTIVE_CHANNEL = 'SET_ACTIVE_CHANNEL';
|
export const SET_ACTIVE_CHANNEL = 'SET_ACTIVE_CHANNEL';
|
||||||
export const SET_INCOGNITO = 'SET_INCOGNITO';
|
export const SET_INCOGNITO = 'SET_INCOGNITO';
|
||||||
|
export const SET_MOBILE_PLAYER_DIMENSIONS = 'SET_MOBILE_PLAYER_DIMENSIONS';
|
||||||
export const RELOAD_REQUIRED = 'RELOAD_REQUIRED';
|
export const RELOAD_REQUIRED = 'RELOAD_REQUIRED';
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { selectCostInfoForUri, doFetchCostInfoForUri } from 'lbryinc';
|
||||||
import { selectShowMatureContent, selectClientSetting } from 'redux/selectors/settings';
|
import { selectShowMatureContent, selectClientSetting } from 'redux/selectors/settings';
|
||||||
import { makeSelectFileRenderModeForUri, makeSelectContentPositionForUri } from 'redux/selectors/content';
|
import { makeSelectFileRenderModeForUri, makeSelectContentPositionForUri } from 'redux/selectors/content';
|
||||||
import { DISABLE_COMMENTS_TAG } from 'constants/tags';
|
import { DISABLE_COMMENTS_TAG } from 'constants/tags';
|
||||||
|
import { doSetMobilePlayerDimensions } from 'redux/actions/app';
|
||||||
|
|
||||||
import FilePage from './view';
|
import FilePage from './view';
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@ const perform = {
|
||||||
doSetContentHistoryItem,
|
doSetContentHistoryItem,
|
||||||
doSetPrimaryUri,
|
doSetPrimaryUri,
|
||||||
clearPosition,
|
clearPosition,
|
||||||
|
doSetMobilePlayerDimensions,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withRouter(connect(select, perform)(FilePage));
|
export default withRouter(connect(select, perform)(FilePage));
|
||||||
|
|
|
@ -15,6 +15,9 @@ import CollectionContent from 'component/collectionContentSidebar';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import I18nMessage from 'component/i18nMessage';
|
import I18nMessage from 'component/i18nMessage';
|
||||||
import Empty from 'component/common/empty';
|
import Empty from 'component/common/empty';
|
||||||
|
import SwipeableDrawer from 'component/swipeableDrawer';
|
||||||
|
import * as ICONS from 'constants/icons';
|
||||||
|
import { useIsMobile } from 'effects/use-screensize';
|
||||||
|
|
||||||
const CommentsList = lazyImport(() => import('component/commentsList' /* webpackChunkName: "comments" */));
|
const CommentsList = lazyImport(() => import('component/commentsList' /* webpackChunkName: "comments" */));
|
||||||
const PostViewer = lazyImport(() => import('component/postViewer' /* webpackChunkName: "postViewer" */));
|
const PostViewer = lazyImport(() => import('component/postViewer' /* webpackChunkName: "postViewer" */));
|
||||||
|
@ -65,6 +68,12 @@ export default function FilePage(props: Props) {
|
||||||
clearPosition,
|
clearPosition,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
|
const [showComments, setShowComments] = React.useState(undefined);
|
||||||
|
const [commentListTitle, setCommentListTitle] = React.useState();
|
||||||
|
|
||||||
|
const drawerWasToggled = showComments !== undefined;
|
||||||
const cost = costInfo ? costInfo.cost : null;
|
const cost = costInfo ? costInfo.cost : null;
|
||||||
const hasFileInfo = fileInfo !== undefined;
|
const hasFileInfo = fileInfo !== undefined;
|
||||||
const isMarkdown = renderMode === RENDER_MODES.MARKDOWN;
|
const isMarkdown = renderMode === RENDER_MODES.MARKDOWN;
|
||||||
|
@ -177,6 +186,12 @@ export default function FilePage(props: Props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const commentsListElement = commentsDisabled ? (
|
||||||
|
<Empty text={__('The creator of this content has disabled comments.')} />
|
||||||
|
) : (
|
||||||
|
<CommentsList uri={uri} linkedCommentId={linkedCommentId} setCommentListTitle={setCommentListTitle} />
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page className="file-page" filePage isMarkdown={isMarkdown}>
|
<Page className="file-page" filePage isMarkdown={isMarkdown}>
|
||||||
<div className={classnames('section card-stack', `file-page__${renderMode}`)}>
|
<div className={classnames('section card-stack', `file-page__${renderMode}`)}>
|
||||||
|
@ -200,15 +215,30 @@ export default function FilePage(props: Props) {
|
||||||
|
|
||||||
{RENDER_MODES.FLOATING_MODES.includes(renderMode) && <FileTitleSection uri={uri} />}
|
{RENDER_MODES.FLOATING_MODES.includes(renderMode) && <FileTitleSection uri={uri} />}
|
||||||
|
|
||||||
{commentsDisabled ? (
|
{isMobile ? (
|
||||||
<Empty text={__('The creator of this content has disabled comments.')} />
|
<SwipeableDrawer
|
||||||
|
open={Boolean(showComments)}
|
||||||
|
toggleDrawer={() => setShowComments(!showComments)}
|
||||||
|
title={commentListTitle}
|
||||||
|
didInitialDisplay={drawerWasToggled}
|
||||||
|
>
|
||||||
|
{commentsListElement}
|
||||||
|
</SwipeableDrawer>
|
||||||
) : (
|
) : (
|
||||||
<React.Suspense fallback={null}>
|
commentsListElement
|
||||||
<CommentsList uri={uri} linkedCommentId={linkedCommentId} />
|
|
||||||
</React.Suspense>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
||||||
|
{isMobile && (
|
||||||
|
<Button
|
||||||
|
className="swipeable-drawer__expand-button"
|
||||||
|
label={commentListTitle}
|
||||||
|
button="primary"
|
||||||
|
icon={ICONS.CHAT}
|
||||||
|
onClick={() => setShowComments(!showComments)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{!isMarkdown && videoTheaterMode && <RightSideContent {...rightSideProps} />}
|
{!isMarkdown && videoTheaterMode && <RightSideContent {...rightSideProps} />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -736,3 +736,8 @@ export function doSetIncognito(incognitoEnabled) {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const doSetMobilePlayerDimensions = (height, width) => ({
|
||||||
|
type: ACTIONS.SET_MOBILE_PLAYER_DIMENSIONS,
|
||||||
|
data: { heightWidth: { height, width } },
|
||||||
|
});
|
||||||
|
|
|
@ -324,6 +324,13 @@ reducers[ACTIONS.SET_INCOGNITO] = (state, action) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.SET_MOBILE_PLAYER_DIMENSIONS] = (state, action) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
mobilePlayerDimensions: action.data.heightWidth,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
reducers[ACTIONS.USER_STATE_POPULATE] = (state, action) => {
|
reducers[ACTIONS.USER_STATE_POPULATE] = (state, action) => {
|
||||||
const { welcomeVersion, allowAnalytics } = action.data;
|
const { welcomeVersion, allowAnalytics } = action.data;
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -106,3 +106,5 @@ export const selectActiveChannelStakedLevel = (state) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const selectIncognito = (state) => selectState(state).incognito;
|
export const selectIncognito = (state) => selectState(state).incognito;
|
||||||
|
|
||||||
|
export const selectMobilePlayerDimensions = (state) => selectState(state).mobilePlayerDimensions;
|
||||||
|
|
|
@ -68,4 +68,5 @@
|
||||||
@import 'component/stripe-card';
|
@import 'component/stripe-card';
|
||||||
@import 'component/wallet-tip-send';
|
@import 'component/wallet-tip-send';
|
||||||
@import 'component/swipe-list';
|
@import 'component/swipe-list';
|
||||||
|
@import 'component/swipeable-drawer';
|
||||||
@import 'component/utils';
|
@import 'component/utils';
|
||||||
|
|
|
@ -13,6 +13,15 @@
|
||||||
.card--enable-overflow {
|
.card--enable-overflow {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
margin-bottom: var(--spacing-m);
|
margin-bottom: var(--spacing-m);
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-small) {
|
||||||
|
overflow-y: scroll;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.card__main-actions {
|
||||||
|
margin-top: 0px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card--disabled {
|
.card--disabled {
|
||||||
|
|
|
@ -27,6 +27,8 @@ $recent-msg-button__height: 2rem;
|
||||||
|
|
||||||
@media (max-width: $breakpoint-small) {
|
@media (max-width: $breakpoint-small) {
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
|
height: 100%;
|
||||||
|
margin-bottom: 0px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +99,6 @@ $recent-msg-button__height: 2rem;
|
||||||
.livestreamComments__wrapper {
|
.livestreamComments__wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: calc(100vh - var(--header-height) - #{$discussion-header__height});
|
|
||||||
|
|
||||||
.main--empty {
|
.main--empty {
|
||||||
.yrbl__wrap {
|
.yrbl__wrap {
|
||||||
|
@ -109,11 +110,20 @@ $recent-msg-button__height: 2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: $breakpoint-small) {
|
||||||
|
height: calc(100% - var(--header-height) - #{$discussion-header__height});
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-small) {
|
||||||
|
height: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.livestream__comments {
|
.livestream__comments {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column-reverse;
|
flex-direction: column;
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-small);
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
overflow-x: visible;
|
overflow-x: visible;
|
||||||
|
@ -138,6 +148,10 @@ $recent-msg-button__height: 2rem;
|
||||||
padding: var(--spacing-s);
|
padding: var(--spacing-s);
|
||||||
border-top: 1px solid var(--color-border);
|
border-top: 1px solid var(--color-border);
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-small) {
|
||||||
|
padding: var(--spacing-xxs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.livestreamSuperchats__wrapper {
|
.livestreamSuperchats__wrapper {
|
||||||
|
@ -153,6 +167,23 @@ $recent-msg-button__height: 2rem;
|
||||||
padding: var(--spacing-xs);
|
padding: var(--spacing-xs);
|
||||||
width: var(--livestream-comments-width);
|
width: var(--livestream-comments-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-small) {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1300;
|
||||||
|
width: 100%;
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0px;
|
||||||
|
padding-left: var(--spacing-xxs);
|
||||||
|
padding-top: var(--spacing-xxs);
|
||||||
|
border-bottom: none;
|
||||||
|
scrollbar-width: 0px;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 0px;
|
||||||
|
height: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.livestreamPinned__wrapper {
|
.livestreamPinned__wrapper {
|
||||||
|
@ -209,6 +240,17 @@ $recent-msg-button__height: 2rem;
|
||||||
.channel-thumbnail {
|
.channel-thumbnail {
|
||||||
margin-right: var(--spacing-xs);
|
margin-right: var(--spacing-xs);
|
||||||
@include handleChannelGif(2rem);
|
@include handleChannelGif(2rem);
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-small) {
|
||||||
|
margin-right: var(--spacing-xxs);
|
||||||
|
@include handleChannelGif(1.5rem);
|
||||||
|
|
||||||
|
.channel-staked__wrapper {
|
||||||
|
padding: 0px;
|
||||||
|
left: -0.4rem;
|
||||||
|
bottom: -0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:first-of-type {
|
&:first-of-type {
|
||||||
|
@ -236,6 +278,15 @@ $recent-msg-button__height: 2rem;
|
||||||
.channel-name {
|
.channel-name {
|
||||||
max-width: 5rem;
|
max-width: 5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-small) {
|
||||||
|
padding: 5px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: var(--font-xxsmall);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.livestreamSuperchat__info {
|
.livestreamSuperchat__info {
|
||||||
|
@ -247,6 +298,10 @@ $recent-msg-button__height: 2rem;
|
||||||
.button {
|
.button {
|
||||||
margin-top: calc(var(--spacing-xxs) / 2);
|
margin-top: calc(var(--spacing-xxs) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-small) {
|
||||||
|
max-height: 40px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.livestreamSuperchat__info--sticker {
|
.livestreamSuperchat__info--sticker {
|
||||||
|
|
|
@ -36,6 +36,11 @@
|
||||||
|
|
||||||
.main-wrapper__inner--filepage {
|
.main-wrapper__inner--filepage {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-small) {
|
||||||
|
margin-top: 0px;
|
||||||
|
padding-top: var(--header-height-mobile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-wrapper__inner--theater-mode {
|
.main-wrapper__inner--theater-mode {
|
||||||
|
@ -227,9 +232,13 @@
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
margin-bottom: var(--spacing-xxs) !important;
|
margin-bottom: 0px !important;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-page__recommended {
|
||||||
|
margin-top: var(--spacing-xxs) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,13 +293,13 @@
|
||||||
margin-top: var(--spacing-m);
|
margin-top: var(--spacing-m);
|
||||||
margin-bottom: var(--spacing-m);
|
margin-bottom: var(--spacing-m);
|
||||||
|
|
||||||
@media (min-width: $breakpoint-large + 300px) {
|
@media (min-width: ($breakpoint-large + 300px)) {
|
||||||
max-width: calc(var(--page-max-width--filepage) / 1.25);
|
max-width: calc(var(--page-max-width--filepage) / 1.25);
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: $breakpoint-medium) and (max-width: $breakpoint-large + 300px) {
|
@media (min-width: $breakpoint-medium) and (max-width: ($breakpoint-large + 300px)) {
|
||||||
max-width: calc(100vw - var(--livestream-comments-width) - var(--spacing-m) * 3);
|
max-width: calc(100vw - var(--livestream-comments-width) - var(--spacing-m) * 3);
|
||||||
margin-left: var(--spacing-m);
|
margin-left: var(--spacing-m);
|
||||||
margin-right: var(--spacing-m);
|
margin-right: var(--spacing-m);
|
||||||
|
|
|
@ -68,6 +68,8 @@
|
||||||
|
|
||||||
@media (max-width: $breakpoint-small) {
|
@media (max-width: $breakpoint-small) {
|
||||||
font-size: var(--font-xsmall);
|
font-size: var(--font-xsmall);
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
.button__content {
|
.button__content {
|
||||||
|
|
40
ui/scss/component/_swipeable-drawer.scss
Normal file
40
ui/scss/component/_swipeable-drawer.scss
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
.swipeable-drawer__header {
|
||||||
|
position: absolute;
|
||||||
|
visibility: visible;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
background-color: var(--color-card-background);
|
||||||
|
|
||||||
|
.button--close {
|
||||||
|
top: 2px !important;
|
||||||
|
right: 2px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.swipeable-drawer__puller {
|
||||||
|
width: 30px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: calc(50% - 15px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.swipeable-drawer__expand-button {
|
||||||
|
width: 100%;
|
||||||
|
margin: var(--spacing-xxs) 0;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swipeable-drawer__expand {
|
||||||
|
border-top: 1px solid var(--color-border);
|
||||||
|
position: fixed;
|
||||||
|
background-color: var(--color-card-background);
|
||||||
|
visibility: visible;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
Loading…
Reference in a new issue