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:
Rafael 2022-02-02 09:44:33 -03:00 committed by Thomas Zarebczan
parent ce11a4b9c1
commit baf51f9c32
21 changed files with 432 additions and 96 deletions

View file

@ -4,6 +4,7 @@
.*/node_modules/react-plastic/.*
.*/node_modules/raf-schd/.*
.*/node_modules/react-beautiful-dnd/.*
.*/node_modules/@emotion/.*
[include]

View file

@ -49,6 +49,7 @@ type Props = {
commentsAreExpanded?: boolean,
fetchReacts: (Array<string>) => Promise<any>,
doResolveUris: (Array<string>) => void,
setCommentListTitle?: (string) => void,
fetchTopLevelComments: (string, number, number, number) => void,
fetchComment: (string) => void,
resetComments: (string) => void,
@ -77,6 +78,7 @@ function CommentList(props: Props) {
commentsAreExpanded,
fetchReacts,
doResolveUris,
setCommentListTitle,
fetchTopLevelComments,
fetchComment,
resetComments,
@ -84,13 +86,13 @@ function CommentList(props: Props) {
const isMobile = useIsMobile();
const isMediumScreen = useIsMediumScreen();
const desktopView = !isMobile && !isMediumScreen;
const spinnerRef = React.useRef();
const DEFAULT_SORT = ENABLE_COMMENT_REACTIONS ? SORT_BY.POPULARITY : SORT_BY.NEWEST;
const [sort, setSort] = usePersistedState('comment-sort-by', DEFAULT_SORT);
const [page, setPage] = React.useState(0);
const [commentsToDisplay, setCommentsToDisplay] = React.useState(topLevelComments);
const hasDefaultExpansion = commentsAreExpanded || desktopView;
const hasDefaultExpansion = commentsAreExpanded || !isMediumScreen || isMobile;
const [expandedComments, setExpandedComments] = React.useState(hasDefaultExpansion);
const totalFetchedComments = allCommentIds ? allCommentIds.length : 0;
const channelId = getChannelIdFromClaim(claim);
@ -99,6 +101,11 @@ function CommentList(props: Props) {
const isResolvingComments = topLevelComments && resolvedComments.length !== topLevelComments.length;
const alreadyResolved = !isResolvingComments && resolvedComments.length !== 0;
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
// If not, wait to show comments until reactions are fetched
@ -283,11 +290,7 @@ function CommentList(props: Props) {
return (
<Card
className="card--enable-overflow"
title={
(totalComments === 0 && __('Leave a comment')) ||
(totalComments === 1 && __('1 comment')) ||
__('%total_comments% comments', { total_comments: totalComments })
}
title={!isMobile && title}
titleActions={
<>
{totalComments > 1 && ENABLE_COMMENT_REACTIONS && (
@ -310,8 +313,8 @@ function CommentList(props: Props) {
<ul
className={classnames({
comments: desktopView || expandedComments,
'comments--contracted': !desktopView && !expandedComments,
comments: isMediumScreen || expandedComments,
'comments--contracted': !isMediumScreen && !expandedComments,
})}
>
{readyToDisplayComments && pinnedComments && getCommentElems(pinnedComments)}

View file

@ -8,10 +8,12 @@ import {
import { selectPlayingUri, makeSelectFileRenderModeForUri } from 'redux/selectors/content';
import { selectCostInfoForUri } from 'lbryinc';
import { doPlayUri } from 'redux/actions/content';
import { doSetMobilePlayerDimensions } from 'redux/actions/app';
import { withRouter } from 'react-router';
import { getChannelIdFromClaim } from 'util/claim';
import { selectActiveLivestreamForChannel } from 'redux/selectors/livestream';
import FileRenderMobile from './view';
import { selectMobilePlayerDimensions } from 'redux/selectors/app';
const select = (state, props) => {
const playingUri = selectPlayingUri(state);
@ -34,11 +36,14 @@ const select = (state, props) => {
activeLivestreamForChannel: channelClaimId && selectActiveLivestreamForChannel(state, channelClaimId),
claimId,
channelClaimId,
mobilePlayerDimensions: selectMobilePlayerDimensions(state),
playingUri,
};
};
const perform = {
doPlayUri,
doSetMobilePlayerDimensions,
};
export default withRouter(connect(select, perform)(FileRenderMobile));

View file

@ -11,6 +11,7 @@ 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';
export const HEADER_HEIGHT_MOBILE = 56;
// ****************************************************************************
// ****************************************************************************
@ -27,7 +28,10 @@ type Props = {
previousListUri: string,
activeLivestreamForChannel?: any,
channelClaimId?: any,
playingUri?: PlayingUri,
mobilePlayerDimensions?: any,
doPlayUri: (string) => void,
doSetMobilePlayerDimensions: (height: number, width: number) => void,
};
export default function FileRenderMobile(props: Props) {
@ -43,7 +47,10 @@ export default function FileRenderMobile(props: Props) {
previousListUri,
activeLivestreamForChannel,
channelClaimId,
playingUri,
mobilePlayerDimensions,
doPlayUri,
doSetMobilePlayerDimensions,
} = props;
const { push } = useHistory();
@ -58,6 +65,7 @@ export default function FileRenderMobile(props: Props) {
const canViewFile = isFree || claimWasPurchased;
const isPlayable = RENDER_MODES.FLOATING_MODES.includes(renderMode) || activeLivestreamForChannel;
const isReadyToPlay = isPlayable && streamingUrl;
const isCurrentMediaPlaying = playingUri && playingUri.uri === uri;
const handleResize = React.useCallback(() => {
const element = document.querySelector(`.${PRIMARY_PLAYER_WRAPPER_CLASS}`);
@ -80,7 +88,11 @@ export default function FileRenderMobile(props: Props) {
// $FlowFixMe
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
// (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);
}, [doNavigate, doPlay, nextListUri, playNextUrl, previousListUri]);
if (!isPlayable || !uri || countdownCanceled || (collectionId && !canViewFile && !nextListUri)) {
if (
!isCurrentMediaPlaying ||
!isPlayable ||
!uri ||
countdownCanceled ||
(collectionId && !canViewFile && !nextListUri)
) {
return null;
}
@ -146,32 +164,28 @@ export default function FileRenderMobile(props: Props) {
}
>
<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>
{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>
) : (
<LoadingScreen status={__('Loading')} />
)}
</div>
</div>
);
}
const Loading = () => <LoadingScreen status={__('Loading')} />;

View file

@ -41,6 +41,7 @@ type Props = {
pinnedComments: Array<Comment>,
superChats: Array<Comment>,
uri: string,
hideHeader?: boolean,
doCommentList: (string, string, number, number) => void,
doResolveUris: (Array<string>, boolean) => void,
doSuperChatList: (string) => void,
@ -55,6 +56,7 @@ export default function LivestreamChatLayout(props: Props) {
pinnedComments,
superChats: superChatsByAmount,
uri,
hideHeader,
doCommentList,
doResolveUris,
doSuperChatList,
@ -242,60 +244,62 @@ export default function LivestreamChatLayout(props: Props) {
return (
<div className={classnames('card livestream__chat', { 'livestream__chat--popout': isPopoutWindow })}>
<div className="card__header--between livestreamDiscussion__header">
<div className="card__title-section--small livestreamDiscussion__title">
{__('Live Chat')}
{!hideHeader && (
<div className="card__header--between livestreamDiscussion__header">
<div className="card__title-section--small livestreamDiscussion__title">
{__('Live Chat')}
<Menu>
<MenuButton className="menu__button">
<Icon size={18} icon={ICONS.SETTINGS} />
</MenuButton>
<Menu>
<MenuButton className="menu__button">
<Icon size={18} icon={ICONS.SETTINGS} />
</MenuButton>
<MenuList className="menu__list">
<MenuItem className="comment__menu-option" onSelect={TOGGLE_TIMESTAMP_OPACITY}>
<span className="menu__link">
<Icon aria-hidden icon={ICONS.TIME} />
{__('Toggle Timestamps')}
</span>
</MenuItem>
<MenuList className="menu__list">
<MenuItem className="comment__menu-option" onSelect={TOGGLE_TIMESTAMP_OPACITY}>
<span className="menu__link">
<Icon aria-hidden icon={ICONS.TIME} />
{__('Toggle Timestamps')}
</span>
</MenuItem>
<MenuItem className="comment__menu-option" onSelect={() => setChatHidden(true)}>
<span className="menu__link">
<Icon aria-hidden icon={ICONS.EYE} />
{__('Hide Chat')}
</span>
</MenuItem>
<MenuItem className="comment__menu-option" onSelect={() => setChatHidden(true)}>
<span className="menu__link">
<Icon aria-hidden icon={ICONS.EYE} />
{__('Hide Chat')}
</span>
</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}>
<span className="menu__link">
<Icon aria-hidden icon={ICONS.EXTERNAL} />
{__('Popout Chat')}
</span>
</MenuItem>
<CreditAmount amount={superChatsLBCAmount || 0} size={8} /> /
<CreditAmount amount={superChatsFiatAmount || 0} size={8} isFiat /> {__('Tipped')}
</>
)}
</MenuList>
</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">
{viewMode === VIEW_MODES.CHAT && superChatsByAmount && (

View file

@ -45,9 +45,12 @@ export default function LivestreamComments(props: Props) {
if (!fetchingComments && commentsToDisplay && commentsToDisplay.length > 0) {
return (
<div className="livestream__comments">
{commentsToDisplay.map((comment) => (
<LivestreamComment comment={comment} key={comment.comment_id} uri={uri} forceUpdate={forceUpdate} />
))}
{commentsToDisplay
.slice(0)
.reverse()
.map((comment) => (
<LivestreamComment comment={comment} key={comment.comment_id} uri={uri} forceUpdate={forceUpdate} />
))}
</div>
);
}

View file

@ -7,6 +7,9 @@ import React from 'react';
import { PRIMARY_PLAYER_WRAPPER_CLASS } from 'page/file/view';
import FileRenderInitiator from 'component/fileRenderInitiator';
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" */));
@ -35,6 +38,9 @@ export default function LivestreamLayout(props: Props) {
const isMobile = useIsMobile();
const [showChat, setShowChat] = React.useState(undefined);
const drawerWasToggled = showChat !== undefined;
if (!claim || !claim.signing_channel) return null;
const { name: channelName, claim_id: channelClaimId } = claim.signing_channel;
@ -85,10 +91,27 @@ export default function LivestreamLayout(props: Props) {
{isMobile && !hideComments && (
<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>
)}
{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} />
</div>
</>

View 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);

View 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>
</>
);
}

View file

@ -32,6 +32,7 @@ export const TOGGLE_YOUTUBE_SYNC_INTEREST = 'TOGGLE_YOUTUBE_SYNC_INTEREST';
export const TOGGLE_SPLASH_ANIMATION = 'TOGGLE_SPLASH_ANIMATION';
export const SET_ACTIVE_CHANNEL = 'SET_ACTIVE_CHANNEL';
export const SET_INCOGNITO = 'SET_INCOGNITO';
export const SET_MOBILE_PLAYER_DIMENSIONS = 'SET_MOBILE_PLAYER_DIMENSIONS';
export const RELOAD_REQUIRED = 'RELOAD_REQUIRED';
// Navigation

View file

@ -14,6 +14,7 @@ import { selectCostInfoForUri, doFetchCostInfoForUri } from 'lbryinc';
import { selectShowMatureContent, selectClientSetting } from 'redux/selectors/settings';
import { makeSelectFileRenderModeForUri, makeSelectContentPositionForUri } from 'redux/selectors/content';
import { DISABLE_COMMENTS_TAG } from 'constants/tags';
import { doSetMobilePlayerDimensions } from 'redux/actions/app';
import FilePage from './view';
@ -45,6 +46,7 @@ const perform = {
doSetContentHistoryItem,
doSetPrimaryUri,
clearPosition,
doSetMobilePlayerDimensions,
};
export default withRouter(connect(select, perform)(FilePage));

View file

@ -15,6 +15,9 @@ import CollectionContent from 'component/collectionContentSidebar';
import Button from 'component/button';
import I18nMessage from 'component/i18nMessage';
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 PostViewer = lazyImport(() => import('component/postViewer' /* webpackChunkName: "postViewer" */));
@ -65,6 +68,12 @@ export default function FilePage(props: Props) {
clearPosition,
} = 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 hasFileInfo = fileInfo !== undefined;
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 (
<Page className="file-page" filePage isMarkdown={isMarkdown}>
<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} />}
{commentsDisabled ? (
<Empty text={__('The creator of this content has disabled comments.')} />
{isMobile ? (
<SwipeableDrawer
open={Boolean(showComments)}
toggleDrawer={() => setShowComments(!showComments)}
title={commentListTitle}
didInitialDisplay={drawerWasToggled}
>
{commentsListElement}
</SwipeableDrawer>
) : (
<React.Suspense fallback={null}>
<CommentsList uri={uri} linkedCommentId={linkedCommentId} />
</React.Suspense>
commentsListElement
)}
</>
{isMobile && (
<Button
className="swipeable-drawer__expand-button"
label={commentListTitle}
button="primary"
icon={ICONS.CHAT}
onClick={() => setShowComments(!showComments)}
/>
)}
{!isMarkdown && videoTheaterMode && <RightSideContent {...rightSideProps} />}
</div>
)}

View file

@ -736,3 +736,8 @@ export function doSetIncognito(incognitoEnabled) {
},
};
}
export const doSetMobilePlayerDimensions = (height, width) => ({
type: ACTIONS.SET_MOBILE_PLAYER_DIMENSIONS,
data: { heightWidth: { height, width } },
});

View file

@ -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) => {
const { welcomeVersion, allowAnalytics } = action.data;
return {

View file

@ -106,3 +106,5 @@ export const selectActiveChannelStakedLevel = (state) => {
};
export const selectIncognito = (state) => selectState(state).incognito;
export const selectMobilePlayerDimensions = (state) => selectState(state).mobilePlayerDimensions;

View file

@ -68,4 +68,5 @@
@import 'component/stripe-card';
@import 'component/wallet-tip-send';
@import 'component/swipe-list';
@import 'component/swipeable-drawer';
@import 'component/utils';

View file

@ -13,6 +13,15 @@
.card--enable-overflow {
overflow: visible;
margin-bottom: var(--spacing-m);
@media (max-width: $breakpoint-small) {
overflow-y: scroll;
height: 100%;
.card__main-actions {
margin-top: 0px !important;
}
}
}
.card--disabled {

View file

@ -27,6 +27,8 @@ $recent-msg-button__height: 2rem;
@media (max-width: $breakpoint-small) {
margin: 0 !important;
height: 100%;
margin-bottom: 0px !important;
}
}
@ -97,7 +99,6 @@ $recent-msg-button__height: 2rem;
.livestreamComments__wrapper {
display: flex;
flex-direction: column;
height: calc(100vh - var(--header-height) - #{$discussion-header__height});
.main--empty {
.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 {
display: flex;
flex-direction: column-reverse;
flex-direction: column;
font-size: var(--font-small);
overflow-y: scroll;
overflow-x: visible;
@ -138,6 +148,10 @@ $recent-msg-button__height: 2rem;
padding: var(--spacing-s);
border-top: 1px solid var(--color-border);
margin-top: auto;
@media (max-width: $breakpoint-small) {
padding: var(--spacing-xxs);
}
}
.livestreamSuperchats__wrapper {
@ -153,6 +167,23 @@ $recent-msg-button__height: 2rem;
padding: var(--spacing-xs);
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 {
@ -209,6 +240,17 @@ $recent-msg-button__height: 2rem;
.channel-thumbnail {
margin-right: var(--spacing-xs);
@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 {
@ -236,6 +278,15 @@ $recent-msg-button__height: 2rem;
.channel-name {
max-width: 5rem;
}
@media (max-width: $breakpoint-small) {
padding: 5px;
padding-bottom: 2px;
span {
font-size: var(--font-xxsmall);
}
}
}
.livestreamSuperchat__info {
@ -247,6 +298,10 @@ $recent-msg-button__height: 2rem;
.button {
margin-top: calc(var(--spacing-xxs) / 2);
}
@media (max-width: $breakpoint-small) {
max-height: 40px;
}
}
.livestreamSuperchat__info--sticker {

View file

@ -36,6 +36,11 @@
.main-wrapper__inner--filepage {
padding: 0;
@media (max-width: $breakpoint-small) {
margin-top: 0px;
padding-top: var(--header-height-mobile);
}
}
.main-wrapper__inner--theater-mode {
@ -227,9 +232,13 @@
.card {
border-radius: 0;
margin-bottom: var(--spacing-xxs) !important;
margin-bottom: 0px !important;
padding: 0;
}
.file-page__recommended {
margin-top: var(--spacing-xxs) !important;
}
}
}
@ -284,13 +293,13 @@
margin-top: 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);
margin-left: 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);
margin-left: var(--spacing-m);
margin-right: var(--spacing-m);

View file

@ -68,6 +68,8 @@
@media (max-width: $breakpoint-small) {
font-size: var(--font-xsmall);
word-wrap: break-word;
overflow: hidden;
a {
.button__content {

View 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;
}