Fixes
Rename FileReactions index import Fix fileAction undefined logic Fix live comment menu Fix superchats
This commit is contained in:
parent
baf51f9c32
commit
c1b84368a9
33 changed files with 443 additions and 176 deletions
|
@ -24,10 +24,10 @@ export default function CollectionAddButton(props: Props) {
|
|||
return !isPlayable ? null : (
|
||||
<Tooltip title={__('Add this claim to a list')} arrow={false}>
|
||||
<Button
|
||||
button={!fileAction && 'alt'}
|
||||
button={!fileAction ? 'alt' : undefined}
|
||||
className={classnames({ 'button--file-action': fileAction })}
|
||||
icon={fileAction ? (!isSaved ? ICONS.ADD : ICONS.STACK) : ICONS.LIBRARY}
|
||||
iconSize={fileAction && 22}
|
||||
iconSize={fileAction ? 22 : undefined}
|
||||
label={uri ? (!isSaved ? __('Save') : __('Saved')) : __('New List')}
|
||||
requiresAuth
|
||||
onClick={(e) => {
|
||||
|
|
|
@ -6,7 +6,6 @@ import {
|
|||
makeSelectClaimIsPending,
|
||||
makeSelectReflectingClaimForUri,
|
||||
makeSelectClaimWasPurchased,
|
||||
isStreamPlaceholderClaim,
|
||||
selectTitleForUri,
|
||||
selectDateForUri,
|
||||
} from 'redux/selectors/claims';
|
||||
|
@ -20,7 +19,7 @@ import { selectIsActiveLivestreamForUri } from 'redux/selectors/livestream';
|
|||
import { selectLanguage, selectShowMatureContent } from 'redux/selectors/settings';
|
||||
import { makeSelectHasVisitedUri } from 'redux/selectors/content';
|
||||
import { selectIsSubscribedForUri } from 'redux/selectors/subscriptions';
|
||||
import { isClaimNsfw } from 'util/claim';
|
||||
import { isClaimNsfw, isStreamPlaceholderClaim } from 'util/claim';
|
||||
import ClaimPreview from './view';
|
||||
import formatMediaDuration from 'util/formatMediaDuration';
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import * as PAGES from 'constants/pages';
|
||||
import { connect } from 'react-redux';
|
||||
import { selectClaimForUri, makeSelectClaimIsPending, isStreamPlaceholderClaim } from 'redux/selectors/claims';
|
||||
import { selectClaimForUri, makeSelectClaimIsPending } from 'redux/selectors/claims';
|
||||
import { doClearPublish, doPrepareEdit } from 'redux/actions/publish';
|
||||
import { push } from 'connected-react-router';
|
||||
import ClaimPreviewSubtitle from './view';
|
||||
import { doFetchSubCount, selectSubCountForUri } from 'lbryinc';
|
||||
import { isStreamPlaceholderClaim } from 'util/claim';
|
||||
|
||||
const select = (state, props) => {
|
||||
const claim = selectClaimForUri(state, props.uri);
|
||||
|
|
|
@ -4,7 +4,6 @@ import {
|
|||
selectIsUriResolving,
|
||||
getThumbnailFromClaim,
|
||||
selectTitleForUri,
|
||||
isStreamPlaceholderClaim,
|
||||
selectDateForUri,
|
||||
} from 'redux/selectors/claims';
|
||||
import { doFileGet } from 'redux/actions/file';
|
||||
|
@ -12,7 +11,7 @@ import { doResolveUri } from 'redux/actions/claims';
|
|||
import { selectViewCountForUri, selectBanStateForUri } from 'lbryinc';
|
||||
import { selectIsActiveLivestreamForUri } from 'redux/selectors/livestream';
|
||||
import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||
import { isClaimNsfw } from 'util/claim';
|
||||
import { isClaimNsfw, isStreamPlaceholderClaim } from 'util/claim';
|
||||
import ClaimPreviewTile from './view';
|
||||
import formatMediaDuration from 'util/formatMediaDuration';
|
||||
|
||||
|
|
|
@ -21,10 +21,10 @@ export default function ClaimSupportButton(props: Props) {
|
|||
return disableSupport ? null : (
|
||||
<Tooltip title={__('Support this claim')} arrow={false}>
|
||||
<Button
|
||||
button={!fileAction && 'alt'}
|
||||
button={!fileAction ? 'alt' : undefined}
|
||||
className={classnames({ 'button--file-action': fileAction })}
|
||||
icon={ICONS.LBC}
|
||||
iconSize={fileAction && 22}
|
||||
iconSize={fileAction ? 22 : undefined}
|
||||
label={isRepost ? __('Support Repost') : __('Support --[button to support a claim]--')}
|
||||
requiresAuth
|
||||
onClick={() => doOpenModal(MODALS.SEND_TIP, { uri, isSupport: true })}
|
||||
|
|
|
@ -36,6 +36,7 @@ type Props = {
|
|||
pinComment: (string, string, boolean) => Promise<any>,
|
||||
commentModAddDelegate: (string, string, ChannelClaim) => void,
|
||||
setQuickReply: (any) => void,
|
||||
handleDismissPin?: () => void,
|
||||
};
|
||||
|
||||
function CommentMenuList(props: Props) {
|
||||
|
@ -63,6 +64,7 @@ function CommentMenuList(props: Props) {
|
|||
pinComment,
|
||||
commentModAddDelegate,
|
||||
setQuickReply,
|
||||
handleDismissPin,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
|
@ -251,6 +253,13 @@ function CommentMenuList(props: Props) {
|
|||
</MenuItem>
|
||||
)}
|
||||
|
||||
{isPinned && (
|
||||
<MenuItem className="comment__menu-option menu__link" onSelect={handleDismissPin}>
|
||||
<Icon aria-hidden icon={ICONS.DISMISS_ALL} />
|
||||
{__('Dismiss Pin')}
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
{activeChannelClaim && (
|
||||
<div className="comment__menu-active">
|
||||
<ChannelThumbnail xsmall noLazyLoad uri={activeChannelClaim.permanent_url} />
|
||||
|
|
|
@ -3,6 +3,7 @@ import { COMMENT_PAGE_SIZE_TOP_LEVEL, SORT_BY } from 'constants/comment';
|
|||
import { ENABLE_COMMENT_REACTIONS } from 'config';
|
||||
import { getChannelIdFromClaim } from 'util/claim';
|
||||
import { useIsMobile, useIsMediumScreen } from 'effects/use-screensize';
|
||||
import { getCommentsListTitle } from 'util/comments';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import * as REACTION_TYPES from 'constants/reactions';
|
||||
import Button from 'component/button';
|
||||
|
@ -49,7 +50,6 @@ 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,
|
||||
|
@ -78,7 +78,6 @@ function CommentList(props: Props) {
|
|||
commentsAreExpanded,
|
||||
fetchReacts,
|
||||
doResolveUris,
|
||||
setCommentListTitle,
|
||||
fetchTopLevelComments,
|
||||
fetchComment,
|
||||
resetComments,
|
||||
|
@ -101,11 +100,7 @@ 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);
|
||||
const title = getCommentsListTitle(totalComments);
|
||||
|
||||
// Display comments immediately if not fetching reactions
|
||||
// If not, wait to show comments until reactions are fetched
|
||||
|
|
|
@ -2745,4 +2745,24 @@ export const icons = {
|
|||
</svg>
|
||||
);
|
||||
},
|
||||
[ICONS.DISMISS_ALL]: (props: IconProps) => {
|
||||
const { size = 24, color = 'currentColor', ...otherProps } = props;
|
||||
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width={size}
|
||||
height={size}
|
||||
fill={color}
|
||||
stroke={color}
|
||||
strokeWidth="1"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
{...otherProps}
|
||||
>
|
||||
<path d="M5 13h14v-2H5v2zm-2 4h14v-2H3v2zM7 7v2h14V7H7z" />
|
||||
</svg>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -3,7 +3,6 @@ import {
|
|||
selectClaimIsMine,
|
||||
selectClaimForUri,
|
||||
selectHasChannels,
|
||||
selectIsStreamPlaceholderForUri,
|
||||
makeSelectTagInClaimOrChannelForUri,
|
||||
} from 'redux/selectors/claims';
|
||||
import { makeSelectStreamingUrlForUri } from 'redux/selectors/file_info';
|
||||
|
@ -15,6 +14,7 @@ import { doOpenModal } from 'redux/actions/app';
|
|||
import FileActions from './view';
|
||||
import { makeSelectFileRenderModeForUri } from 'redux/selectors/content';
|
||||
import { DISABLE_DOWNLOAD_BUTTON_TAG } from 'constants/tags';
|
||||
import { isStreamPlaceholderClaim } from 'util/claim';
|
||||
|
||||
const select = (state, props) => {
|
||||
const { uri } = props;
|
||||
|
@ -27,7 +27,7 @@ const select = (state, props) => {
|
|||
renderMode: makeSelectFileRenderModeForUri(uri)(state),
|
||||
costInfo: selectCostInfoForUri(state, uri),
|
||||
hasChannels: selectHasChannels(state),
|
||||
isLivestreamClaim: selectIsStreamPlaceholderForUri(state, uri),
|
||||
isLivestreamClaim: isStreamPlaceholderClaim(claim),
|
||||
streamingUrl: makeSelectStreamingUrlForUri(uri)(state),
|
||||
disableDownloadButton: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_DOWNLOAD_BUTTON_TAG)(state),
|
||||
};
|
||||
|
|
|
@ -119,7 +119,7 @@ export default function FileActions(props: Props) {
|
|||
|
||||
<ClaimCollectionAddButton uri={uri} fileAction />
|
||||
|
||||
{!hideRepost && !isMobile && (
|
||||
{!hideRepost && !isMobile && !isLivestreamClaim && (
|
||||
<Tooltip title={__('Repost')} arrow={false}>
|
||||
<Button
|
||||
button="alt"
|
||||
|
@ -183,7 +183,7 @@ export default function FileActions(props: Props) {
|
|||
<MenuList className="menu__list">
|
||||
{isMobile && (
|
||||
<>
|
||||
{!hideRepost && (
|
||||
{!hideRepost && !isLivestreamClaim && (
|
||||
<MenuItem className="comment__menu-option" onSelect={handleRepostClick}>
|
||||
<div className="menu__link">
|
||||
<Icon aria-hidden icon={ICONS.REPOST} />
|
||||
|
|
|
@ -10,8 +10,6 @@ import Button from 'component/button';
|
|||
import LbcSymbol from 'component/common/lbc-symbol';
|
||||
import FileDetails from 'component/fileDetails';
|
||||
import FileValues from 'component/fileValues';
|
||||
import { useIsMobile } from 'effects/use-screensize';
|
||||
|
||||
type Props = {
|
||||
uri: string,
|
||||
expandOverride: boolean,
|
||||
|
@ -28,8 +26,6 @@ type Props = {
|
|||
export default function FileDescription(props: Props) {
|
||||
const { uri, description, amount, hasSupport, isEmpty, doOpenModal, claimIsMine, expandOverride } = props;
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const [expanded, setExpanded] = React.useState(false);
|
||||
const [showCreditDetails, setShowCreditDetails] = React.useState(false);
|
||||
|
||||
|
@ -47,13 +43,11 @@ export default function FileDescription(props: Props) {
|
|||
'media__info-text--expanded': expanded,
|
||||
})}
|
||||
>
|
||||
{isMobile && <ClaimTags uri={uri} type="large" />}
|
||||
|
||||
<div className="mediaInfo__description">
|
||||
{description && (
|
||||
<MarkdownPreview className="markdown-preview--description" content={description} simpleLinks />
|
||||
)}
|
||||
{!isMobile && <ClaimTags uri={uri} type="large" />}
|
||||
<ClaimTags uri={uri} type="large" />
|
||||
<FileDetails uri={uri} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
makeSelectDislikeCountForUri,
|
||||
} from 'redux/selectors/reactions';
|
||||
import { doFetchReactions, doReactionLike, doReactionDislike } from 'redux/actions/reactions';
|
||||
import FileViewCount from './view';
|
||||
import FileReactions from './view';
|
||||
import { selectClaimForUri, selectIsStreamPlaceholderForUri } from 'redux/selectors/claims';
|
||||
|
||||
const select = (state, props) => {
|
||||
|
@ -34,4 +34,4 @@ const perform = {
|
|||
doReactionDislike,
|
||||
};
|
||||
|
||||
export default connect(select, perform)(FileViewCount);
|
||||
export default connect(select, perform)(FileReactions);
|
||||
|
|
|
@ -138,7 +138,7 @@ const FileReaction = (reactionProps: ReactionProps) => {
|
|||
|
||||
return (
|
||||
<Tooltip title={title} arrow={false}>
|
||||
<div style={{ margin: '0' }}>
|
||||
<div className="file-reaction__tooltip-inner">
|
||||
<Button
|
||||
requiresAuth
|
||||
authSrc="filereaction_like"
|
||||
|
|
105
ui/component/livestreamChatLayout/livestream-menu.jsx
Normal file
105
ui/component/livestreamChatLayout/livestream-menu.jsx
Normal file
|
@ -0,0 +1,105 @@
|
|||
// @flow
|
||||
// $FlowFixMe
|
||||
import { Global } from '@emotion/react';
|
||||
|
||||
import { Menu, MenuButton, MenuList, MenuItem } from '@reach/menu-button';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useIsMobile } from 'effects/use-screensize';
|
||||
import usePersistedState from 'effects/use-persisted-state';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import Icon from 'component/common/icon';
|
||||
import React from 'react';
|
||||
|
||||
type Props = {
|
||||
isPopoutWindow?: boolean,
|
||||
superchatsHidden?: boolean,
|
||||
noSuperchats?: boolean,
|
||||
hideChat?: () => void,
|
||||
setPopoutWindow?: (any) => void,
|
||||
toggleSuperchats?: () => void,
|
||||
};
|
||||
|
||||
export default function LivestreamMenu(props: Props) {
|
||||
const { isPopoutWindow, superchatsHidden, noSuperchats, hideChat, setPopoutWindow, toggleSuperchats } = props;
|
||||
|
||||
const {
|
||||
location: { pathname },
|
||||
} = useHistory();
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const [showTimestamps, setShowTimestamps] = usePersistedState('live-timestamps', false);
|
||||
|
||||
function handlePopout() {
|
||||
if (setPopoutWindow) {
|
||||
const newWindow = window.open('/$/popout' + pathname, 'Popout Chat', 'height=700,width=400');
|
||||
|
||||
// Add function to newWindow when closed (either manually or from button component)
|
||||
newWindow.onbeforeunload = () => setPopoutWindow(undefined);
|
||||
|
||||
if (window.focus) newWindow.focus();
|
||||
setPopoutWindow(newWindow);
|
||||
}
|
||||
}
|
||||
|
||||
const MenuGlobalStyles = () => (
|
||||
<Global
|
||||
styles={{
|
||||
':root': {
|
||||
'--live-timestamp-opacity': showTimestamps ? '0.5' : '0',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MenuGlobalStyles />
|
||||
|
||||
<Menu>
|
||||
<MenuButton className="menu__button">
|
||||
<Icon size={isMobile ? 16 : 18} icon={ICONS.SETTINGS} />
|
||||
</MenuButton>
|
||||
|
||||
<MenuList className="menu__list">
|
||||
<MenuItem className="comment__menu-option" onSelect={() => setShowTimestamps(!showTimestamps)}>
|
||||
<span className="menu__link">
|
||||
<Icon aria-hidden icon={ICONS.TIME} />
|
||||
{__('Toggle Timestamps')}
|
||||
</span>
|
||||
</MenuItem>
|
||||
|
||||
{!isMobile ? (
|
||||
<>
|
||||
{/* No need for Hide Chat on mobile with the expand/collapse drawer */}
|
||||
<MenuItem className="comment__menu-option" onSelect={hideChat}>
|
||||
<span className="menu__link">
|
||||
<Icon aria-hidden icon={ICONS.EYE} />
|
||||
{__('Hide Chat')}
|
||||
</span>
|
||||
</MenuItem>
|
||||
|
||||
{!isPopoutWindow && (
|
||||
<MenuItem className="comment__menu-option" onSelect={handlePopout}>
|
||||
<span className="menu__link">
|
||||
<Icon aria-hidden icon={ICONS.EXTERNAL} />
|
||||
{__('Popout Chat')}
|
||||
</span>
|
||||
</MenuItem>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
!noSuperchats && (
|
||||
<MenuItem className="comment__menu-option" onSelect={toggleSuperchats}>
|
||||
<span className="menu__link">
|
||||
<Icon aria-hidden icon={superchatsHidden ? ICONS.EYE : ICONS.DISMISS_ALL} size={18} />
|
||||
{superchatsHidden ? __('Display Superchats') : __('Dismiss Superchats')}
|
||||
</span>
|
||||
</MenuItem>
|
||||
)
|
||||
)}
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -2,29 +2,20 @@
|
|||
import 'scss/component/_livestream-chat.scss';
|
||||
|
||||
import { formatLbryUrlForWeb } from 'util/url';
|
||||
import { Menu, MenuButton, MenuList, MenuItem } from '@reach/menu-button';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useIsMobile } from 'effects/use-screensize';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import Button from 'component/button';
|
||||
import classnames from 'classnames';
|
||||
import CommentCreate from 'component/commentCreate';
|
||||
import CreditAmount from 'component/common/credit-amount';
|
||||
import Icon from 'component/common/icon';
|
||||
import LivestreamComment from 'component/livestreamComment';
|
||||
import LivestreamComments from 'component/livestreamComments';
|
||||
import LivestreamSuperchats from './livestream-superchats';
|
||||
import LivestreamMenu from './livestream-menu';
|
||||
import React from 'react';
|
||||
import Spinner from 'component/spinner';
|
||||
import Yrbl from 'component/yrbl';
|
||||
|
||||
const IS_TIMESTAMP_VISIBLE = () =>
|
||||
// $FlowFixMe
|
||||
document.documentElement.style.getPropertyValue('--live-timestamp-opacity') === '0.5';
|
||||
|
||||
const TOGGLE_TIMESTAMP_OPACITY = () =>
|
||||
// $FlowFixMe
|
||||
document.documentElement.style.setProperty('--live-timestamp-opacity', IS_TIMESTAMP_VISIBLE() ? '0' : '0.5');
|
||||
import { getTipValues } from 'util/livestream';
|
||||
|
||||
const VIEW_MODES = {
|
||||
CHAT: 'chat',
|
||||
|
@ -42,6 +33,8 @@ type Props = {
|
|||
superChats: Array<Comment>,
|
||||
uri: string,
|
||||
hideHeader?: boolean,
|
||||
superchatsHidden?: boolean,
|
||||
customViewMode?: string,
|
||||
doCommentList: (string, string, number, number) => void,
|
||||
doResolveUris: (Array<string>, boolean) => void,
|
||||
doSuperChatList: (string) => void,
|
||||
|
@ -57,15 +50,13 @@ export default function LivestreamChatLayout(props: Props) {
|
|||
superChats: superChatsByAmount,
|
||||
uri,
|
||||
hideHeader,
|
||||
superchatsHidden,
|
||||
customViewMode,
|
||||
doCommentList,
|
||||
doResolveUris,
|
||||
doSuperChatList,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
location: { pathname },
|
||||
} = useHistory();
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const discussionElement = document.querySelector('.livestream__comments');
|
||||
|
@ -90,22 +81,7 @@ export default function LivestreamChatLayout(props: Props) {
|
|||
const commentsToDisplay = viewMode === VIEW_MODES.CHAT ? commentsByChronologicalOrder : superChatsByAmount;
|
||||
const commentsLength = commentsToDisplay && commentsToDisplay.length;
|
||||
const pinnedComment = pinnedComments.length > 0 ? pinnedComments[0] : null;
|
||||
|
||||
let superChatsChannelUrls = [];
|
||||
let superChatsFiatAmount = 0;
|
||||
let superChatsLBCAmount = 0;
|
||||
if (superChatsByAmount) {
|
||||
superChatsByAmount.forEach((superChat) => {
|
||||
const { is_fiat: isFiat, support_amount: tipAmount, channel_url: uri } = superChat;
|
||||
|
||||
if (isFiat) {
|
||||
superChatsFiatAmount = superChatsFiatAmount + tipAmount;
|
||||
} else {
|
||||
superChatsLBCAmount = superChatsLBCAmount + tipAmount;
|
||||
}
|
||||
superChatsChannelUrls.push(uri || '0');
|
||||
});
|
||||
}
|
||||
const { superChatsChannelUrls, superChatsFiatAmount, superChatsLBCAmount } = getTipValues(superChatsByAmount);
|
||||
|
||||
function toggleSuperChat() {
|
||||
if (superChatsChannelUrls && superChatsChannelUrls.length > 0) {
|
||||
|
@ -118,15 +94,11 @@ export default function LivestreamChatLayout(props: Props) {
|
|||
setViewMode(VIEW_MODES.SUPERCHAT);
|
||||
}
|
||||
|
||||
function handlePopout() {
|
||||
const newWindow = window.open('/$/popout' + pathname, 'Popout Chat', 'height=700,width=400');
|
||||
|
||||
// Add function to newWindow when closed (either manually or from button component)
|
||||
newWindow.onbeforeunload = () => setPopoutWindow(undefined);
|
||||
|
||||
if (window.focus) newWindow.focus();
|
||||
setPopoutWindow(newWindow);
|
||||
}
|
||||
React.useEffect(() => {
|
||||
if (customViewMode && customViewMode !== viewMode) {
|
||||
setViewMode(customViewMode);
|
||||
}
|
||||
}, [customViewMode, viewMode]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (claimId) {
|
||||
|
@ -249,38 +221,11 @@ export default function LivestreamChatLayout(props: Props) {
|
|||
<div className="card__title-section--small livestreamDiscussion__title">
|
||||
{__('Live Chat')}
|
||||
|
||||
<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>
|
||||
|
||||
<MenuItem className="comment__menu-option" onSelect={() => setChatHidden(true)}>
|
||||
<span className="menu__link">
|
||||
<Icon aria-hidden icon={ICONS.EYE} />
|
||||
{__('Hide Chat')}
|
||||
</span>
|
||||
</MenuItem>
|
||||
|
||||
{!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>
|
||||
<LivestreamMenu
|
||||
isPopoutWindow={isPopoutWindow}
|
||||
hideChat={() => setChatHidden(true)}
|
||||
setPopoutWindow={(v) => setPopoutWindow(v)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{superChatsByAmount && (
|
||||
|
@ -302,28 +247,33 @@ export default function LivestreamChatLayout(props: Props) {
|
|||
)}
|
||||
|
||||
<div ref={commentsRef} className="livestreamComments__wrapper">
|
||||
{viewMode === VIEW_MODES.CHAT && superChatsByAmount && (
|
||||
<LivestreamSuperchats superChats={superChatsByAmount} toggleSuperChat={toggleSuperChat} />
|
||||
)}
|
||||
<div className="livestream-comments__top-actions">
|
||||
{viewMode === VIEW_MODES.CHAT && superChatsByAmount && !superchatsHidden && (
|
||||
<LivestreamSuperchats superChats={superChatsByAmount} toggleSuperChat={toggleSuperChat} />
|
||||
)}
|
||||
|
||||
{pinnedComment && showPinned && viewMode === VIEW_MODES.CHAT && (
|
||||
<div className="livestreamPinned__wrapper">
|
||||
<LivestreamComment
|
||||
comment={pinnedComment}
|
||||
key={pinnedComment.comment_id}
|
||||
uri={uri}
|
||||
pushMention={setMention}
|
||||
/>
|
||||
{pinnedComment && showPinned && viewMode === VIEW_MODES.CHAT && (
|
||||
<div className="livestreamPinned__wrapper">
|
||||
<LivestreamComment
|
||||
comment={pinnedComment}
|
||||
key={pinnedComment.comment_id}
|
||||
uri={uri}
|
||||
pushMention={setMention}
|
||||
handleDismissPin={() => setShowPinned(false)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
title={__('Dismiss pinned comment')}
|
||||
button="inverse"
|
||||
className="close-button"
|
||||
onClick={() => setShowPinned(false)}
|
||||
icon={ICONS.REMOVE}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{!isMobile && (
|
||||
<Button
|
||||
title={__('Dismiss pinned comment')}
|
||||
button="inverse"
|
||||
className="close-button"
|
||||
onClick={() => setShowPinned(false)}
|
||||
icon={ICONS.REMOVE}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{viewMode === VIEW_MODES.SUPERCHAT && resolvingSuperChats ? (
|
||||
<div className="main--empty">
|
||||
|
|
|
@ -26,10 +26,11 @@ type Props = {
|
|||
claim: StreamClaim,
|
||||
myChannelIds: ?Array<string>,
|
||||
stakedLevel: number,
|
||||
handleDismissPin?: () => void,
|
||||
};
|
||||
|
||||
export default function LivestreamComment(props: Props) {
|
||||
const { comment, forceUpdate, uri, claim, myChannelIds, stakedLevel } = props;
|
||||
const { comment, forceUpdate, uri, claim, myChannelIds, stakedLevel, handleDismissPin } = props;
|
||||
|
||||
const {
|
||||
channel_url: authorUri,
|
||||
|
@ -137,6 +138,7 @@ export default function LivestreamComment(props: Props) {
|
|||
disableEdit
|
||||
disableRemove={comment.removed}
|
||||
isLiveComment
|
||||
handleDismissPin={handleDismissPin}
|
||||
/>
|
||||
</Menu>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { selectClaimForUri, makeSelectTagInClaimOrChannelForUri, selectThumbnailForUri } from 'redux/selectors/claims';
|
||||
import { selectSuperChatsForUri } from 'redux/selectors/comments';
|
||||
import LivestreamLayout from './view';
|
||||
import { DISABLE_COMMENTS_TAG } from 'constants/tags';
|
||||
|
||||
|
@ -10,6 +11,7 @@ const select = (state, props) => {
|
|||
claim: selectClaimForUri(state, uri),
|
||||
thumbnail: selectThumbnailForUri(state, uri),
|
||||
chatDisabled: makeSelectTagInClaimOrChannelForUri(uri, DISABLE_COMMENTS_TAG)(state),
|
||||
superChats: selectSuperChatsForUri(state, uri),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// @flow
|
||||
import { lazyImport } from 'util/lazyImport';
|
||||
import { useIsMobile } from 'effects/use-screensize';
|
||||
import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button';
|
||||
import FileTitleSection from 'component/fileTitleSection';
|
||||
import LivestreamLink from 'component/livestreamLink';
|
||||
import React from 'react';
|
||||
|
@ -10,9 +11,18 @@ import LivestreamIframeRender from './iframe-render';
|
|||
import Button from 'component/button';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import SwipeableDrawer from 'component/swipeableDrawer';
|
||||
import LivestreamMenu from 'component/livestreamChatLayout/livestream-menu';
|
||||
import Icon from 'component/common/icon';
|
||||
import CreditAmount from 'component/common/credit-amount';
|
||||
import { getTipValues } from 'util/livestream';
|
||||
|
||||
const LivestreamChatLayout = lazyImport(() => import('component/livestreamChatLayout' /* webpackChunkName: "chat" */));
|
||||
|
||||
const VIEW_MODES = {
|
||||
CHAT: 'chat',
|
||||
SUPERCHAT: 'sc',
|
||||
};
|
||||
|
||||
type Props = {
|
||||
activeStreamUri: boolean | string,
|
||||
claim: ?StreamClaim,
|
||||
|
@ -22,6 +32,7 @@ type Props = {
|
|||
showLivestream: boolean,
|
||||
showScheduledInfo: boolean,
|
||||
uri: string,
|
||||
superChats: Array<Comment>,
|
||||
};
|
||||
|
||||
export default function LivestreamLayout(props: Props) {
|
||||
|
@ -34,16 +45,43 @@ export default function LivestreamLayout(props: Props) {
|
|||
showLivestream,
|
||||
showScheduledInfo,
|
||||
uri,
|
||||
superChats,
|
||||
} = props;
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const [showChat, setShowChat] = React.useState(undefined);
|
||||
const drawerWasToggled = showChat !== undefined;
|
||||
const [superchatsHidden, setSuperchatsHidden] = React.useState(false);
|
||||
const [chatViewMode, setChatViewMode] = React.useState(VIEW_MODES.CHAT);
|
||||
|
||||
if (!claim || !claim.signing_channel) return null;
|
||||
|
||||
const { name: channelName, claim_id: channelClaimId } = claim.signing_channel;
|
||||
const { superChatsFiatAmount, superChatsLBCAmount } = getTipValues(superChats);
|
||||
|
||||
const ChatModeSelector = () => (
|
||||
<Menu>
|
||||
<MenuButton>
|
||||
<span className="swipeable-drawer__title-menu">
|
||||
{chatViewMode === VIEW_MODES.CHAT ? __('Live Chat') : __('Super Chats')}
|
||||
<Icon icon={ICONS.DOWN} />
|
||||
</span>
|
||||
</MenuButton>
|
||||
|
||||
<MenuList className="menu__list--header">
|
||||
<MenuItem className="menu__link" onSelect={() => setChatViewMode(VIEW_MODES.CHAT)}>
|
||||
{__('Live Chat')}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem className="menu__link" onSelect={() => setChatViewMode(VIEW_MODES.SUPERCHAT)}>
|
||||
<div className="recommended-content__toggles">
|
||||
<CreditAmount amount={superChatsLBCAmount || 0} size={8} /> /
|
||||
<CreditAmount amount={superChatsFiatAmount || 0} size={8} isFiat /> {__('Tipped')}
|
||||
</div>
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -94,10 +132,21 @@ export default function LivestreamLayout(props: Props) {
|
|||
<SwipeableDrawer
|
||||
open={Boolean(showChat)}
|
||||
toggleDrawer={() => setShowChat(!showChat)}
|
||||
title={__('Live Chat')}
|
||||
didInitialDisplay={drawerWasToggled}
|
||||
title={<ChatModeSelector />}
|
||||
actions={
|
||||
<LivestreamMenu
|
||||
noSuperchats={!superChats || superChats.length === 0}
|
||||
superchatsHidden={superchatsHidden}
|
||||
toggleSuperchats={() => setSuperchatsHidden(!superchatsHidden)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<LivestreamChatLayout uri={uri} hideHeader />
|
||||
<LivestreamChatLayout
|
||||
uri={uri}
|
||||
hideHeader
|
||||
superchatsHidden={superchatsHidden}
|
||||
customViewMode={chatViewMode}
|
||||
/>
|
||||
</SwipeableDrawer>
|
||||
</React.Suspense>
|
||||
)}
|
||||
|
|
|
@ -3,15 +3,12 @@
|
|||
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;
|
||||
|
||||
|
@ -20,20 +17,20 @@ type Props = {
|
|||
open: Boolean,
|
||||
theme: string,
|
||||
mobilePlayerDimensions?: { height: number },
|
||||
title: string,
|
||||
didInitialDisplay?: boolean,
|
||||
title: any,
|
||||
actions?: any,
|
||||
toggleDrawer: () => void,
|
||||
};
|
||||
|
||||
export default function SwipeableDrawer(props: Props) {
|
||||
const { mobilePlayerDimensions, title, children, open, theme, didInitialDisplay, toggleDrawer } = props;
|
||||
const { mobilePlayerDimensions, title, children, open, theme, actions, toggleDrawer } = props;
|
||||
|
||||
const [coverHeight, setCoverHeight] = React.useState();
|
||||
|
||||
const videoHeight = coverHeight || (mobilePlayerDimensions ? mobilePlayerDimensions.height : 0);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (open && !mobilePlayerDimensions) {
|
||||
if (open && !mobilePlayerDimensions && !coverHeight) {
|
||||
const element = document.querySelector(`.file-page__video-container`);
|
||||
|
||||
if (element) {
|
||||
|
@ -41,15 +38,14 @@ export default function SwipeableDrawer(props: Props) {
|
|||
setCoverHeight(rect.height);
|
||||
}
|
||||
}
|
||||
}, [mobilePlayerDimensions, open]);
|
||||
}, [coverHeight, mobilePlayerDimensions, open]);
|
||||
|
||||
const drawerGlobalStyles = (
|
||||
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',
|
||||
maxHeight: open ? '100vh' : 'unset',
|
||||
},
|
||||
'.MuiDrawer-root': {
|
||||
top: `calc(${HEADER_HEIGHT_MOBILE}px + ${videoHeight}px) !important`,
|
||||
|
@ -68,9 +64,21 @@ export default function SwipeableDrawer(props: Props) {
|
|||
<span className="swipeable-drawer__puller" style={{ backgroundColor: theme === 'light' ? grey[300] : grey[800] }} />
|
||||
);
|
||||
|
||||
const HeaderContents = () => (
|
||||
<div className="swipeable-drawer__header-content">
|
||||
{title}
|
||||
|
||||
<div className="swipeable-drawer__header-actions">
|
||||
{actions}
|
||||
|
||||
<Button icon={ICONS.REMOVE} iconSize={16} onClick={toggleDrawer} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{drawerGlobalStyles}
|
||||
<DrawerGlobalStyles />
|
||||
|
||||
<MUIDrawer
|
||||
anchor="bottom"
|
||||
|
@ -83,22 +91,10 @@ export default function SwipeableDrawer(props: Props) {
|
|||
disableSwipeToOpen
|
||||
ModalProps={{ keepMounted: true }}
|
||||
>
|
||||
{didInitialDisplay && (
|
||||
{open && (
|
||||
<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>
|
||||
)}
|
||||
<Puller />
|
||||
<HeaderContents />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
@ -185,8 +185,9 @@ export const TECHNOLOGY = 'Technology';
|
|||
export const EMOJI = 'Emoji';
|
||||
export const STICKER = 'Sticker';
|
||||
export const EDUCATION = 'Education';
|
||||
export const POP_CULTURE = 'Pop Culture';
|
||||
export const POP_CULTURE = 'PopCulture';
|
||||
export const ODYSEE_LOGO = 'OdyseeLogo';
|
||||
export const ODYSEE_WHITE_TEXT = 'OdyseeLogoWhiteText';
|
||||
export const ODYSEE_DARK_TEXT = 'OdyseeLogoDarkText';
|
||||
export const FEATURED = 'Featured';
|
||||
export const DISMISS_ALL = 'DismissAll';
|
||||
|
|
|
@ -13,6 +13,7 @@ import * as SETTINGS from 'constants/settings';
|
|||
import { selectCostInfoForUri, doFetchCostInfoForUri } from 'lbryinc';
|
||||
import { selectShowMatureContent, selectClientSetting } from 'redux/selectors/settings';
|
||||
import { makeSelectFileRenderModeForUri, makeSelectContentPositionForUri } from 'redux/selectors/content';
|
||||
import { makeSelectCommentsListTitleForUri } from 'redux/selectors/comments';
|
||||
import { DISABLE_COMMENTS_TAG } from 'constants/tags';
|
||||
import { doSetMobilePlayerDimensions } from 'redux/actions/app';
|
||||
|
||||
|
@ -38,6 +39,7 @@ const select = (state, props) => {
|
|||
hasCollectionById: Boolean(makeSelectCollectionForId(collectionId)(state)),
|
||||
collectionId,
|
||||
position: makeSelectContentPositionForUri(uri)(state),
|
||||
commentsListTitle: makeSelectCommentsListTitleForUri(uri)(state),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ type Props = {
|
|||
commentsDisabled: boolean,
|
||||
isLivestream: boolean,
|
||||
position: number,
|
||||
commentsListTitle: string,
|
||||
doFetchCostInfoForUri: (uri: string) => void,
|
||||
doSetContentHistoryItem: (uri: string) => void,
|
||||
doSetPrimaryUri: (uri: ?string) => void,
|
||||
|
@ -62,6 +63,7 @@ export default function FilePage(props: Props) {
|
|||
collectionId,
|
||||
isLivestream,
|
||||
position,
|
||||
commentsListTitle,
|
||||
doFetchCostInfoForUri,
|
||||
doSetContentHistoryItem,
|
||||
doSetPrimaryUri,
|
||||
|
@ -71,9 +73,7 @@ export default function FilePage(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 hasFileInfo = fileInfo !== undefined;
|
||||
const isMarkdown = renderMode === RENDER_MODES.MARKDOWN;
|
||||
|
@ -189,7 +189,7 @@ 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} />
|
||||
<CommentsList uri={uri} linkedCommentId={linkedCommentId} />
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -219,8 +219,7 @@ export default function FilePage(props: Props) {
|
|||
<SwipeableDrawer
|
||||
open={Boolean(showComments)}
|
||||
toggleDrawer={() => setShowComments(!showComments)}
|
||||
title={commentListTitle}
|
||||
didInitialDisplay={drawerWasToggled}
|
||||
title={commentsListTitle}
|
||||
>
|
||||
{commentsListElement}
|
||||
</SwipeableDrawer>
|
||||
|
@ -232,7 +231,7 @@ export default function FilePage(props: Props) {
|
|||
{isMobile && (
|
||||
<Button
|
||||
className="swipeable-drawer__expand-button"
|
||||
label={commentListTitle}
|
||||
label={commentsListTitle}
|
||||
button="primary"
|
||||
icon={ICONS.CHAT}
|
||||
onClick={() => setShowComments(!showComments)}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { normalizeURI, parseURI, isURIValid } from 'util/lbryURI';
|
|||
import { selectSupportsByOutpoint } from 'redux/selectors/wallet';
|
||||
import { createSelector } from 'reselect';
|
||||
import { createCachedSelector } from 're-reselect';
|
||||
import { isClaimNsfw, filterClaims, getChannelIdFromClaim } from 'util/claim';
|
||||
import { isClaimNsfw, filterClaims, getChannelIdFromClaim, isStreamPlaceholderClaim } from 'util/claim';
|
||||
import * as CLAIM from 'constants/claim';
|
||||
import { INTERNAL_TAGS } from 'constants/tags';
|
||||
|
||||
|
@ -592,7 +592,7 @@ export const selectChannelForClaimUri = createCachedSelector(
|
|||
return includePrefix ? permanentUrl : permanentUrl.slice('lbry://'.length);
|
||||
}
|
||||
}
|
||||
)((state, uri) => String(uri));
|
||||
)((state, uri, includePrefix) => `${String(uri)}:${String(includePrefix)}`);
|
||||
|
||||
// Returns the associated channel uri for a given claim uri
|
||||
// accepts a regular claim uri lbry://something
|
||||
|
@ -716,10 +716,6 @@ export const makeSelectClaimHasSource = (uri: string) =>
|
|||
return Boolean(claim.value.source);
|
||||
});
|
||||
|
||||
export const isStreamPlaceholderClaim = (claim: ?StreamClaim) => {
|
||||
return claim ? Boolean(claim.value_type === 'stream' && !claim.value.source) : false;
|
||||
};
|
||||
|
||||
export const selectIsStreamPlaceholderForUri = (state: State, uri: string) => {
|
||||
const claim = selectClaimForUri(state, uri);
|
||||
return isStreamPlaceholderClaim(claim);
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
} from 'redux/selectors/claims';
|
||||
import { isClaimNsfw, getChannelFromClaim } from 'util/claim';
|
||||
import { selectSubscriptionUris } from 'redux/selectors/subscriptions';
|
||||
import { getCommentsListTitle } from 'util/comments';
|
||||
|
||||
type State = { claims: any, comments: CommentsState };
|
||||
|
||||
|
@ -338,9 +339,15 @@ export const makeSelectTotalReplyPagesForParentId = (parentId: string) =>
|
|||
export const makeSelectTotalCommentsCountForUri = (uri: string) =>
|
||||
createSelector(selectState, selectCommentsByUri, (state, byUri) => {
|
||||
const claimId = byUri[uri];
|
||||
|
||||
return state.totalCommentsById[claimId] || 0;
|
||||
});
|
||||
|
||||
export const makeSelectCommentsListTitleForUri = (uri: string) =>
|
||||
createSelector(makeSelectTotalCommentsCountForUri(uri), (totalComments) => {
|
||||
return getCommentsListTitle(totalComments);
|
||||
});
|
||||
|
||||
// Personal list
|
||||
export const makeSelectChannelIsBlocked = (uri: string) =>
|
||||
createSelector(selectModerationBlockList, (blockedChannelUris) => {
|
||||
|
|
|
@ -756,3 +756,7 @@ svg + .button__label {
|
|||
.button--hash-id {
|
||||
@include font-mono;
|
||||
}
|
||||
|
||||
.file-reaction__tooltip-inner {
|
||||
margin: 0px !important;
|
||||
}
|
||||
|
|
|
@ -169,13 +169,10 @@ $recent-msg-button__height: 2rem;
|
|||
}
|
||||
|
||||
@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;
|
||||
|
||||
|
@ -186,10 +183,23 @@ $recent-msg-button__height: 2rem;
|
|||
}
|
||||
}
|
||||
|
||||
.livestream-comments__top-actions {
|
||||
@media (max-width: $breakpoint-small) {
|
||||
position: absolute;
|
||||
display: grid;
|
||||
padding: var(--spacing-xxs);
|
||||
padding-right: var(--spacing-m);
|
||||
|
||||
> div:not(:first-child) {
|
||||
margin-top: var(--spacing-xxs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.livestreamPinned__wrapper {
|
||||
@extend .livestreamSuperchats__wrapper;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
padding: var(--spacing-s) var(--spacing-xs);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
font-size: var(--font-small);
|
||||
|
@ -213,6 +223,46 @@ $recent-msg-button__height: 2rem;
|
|||
padding: var(--spacing-xs);
|
||||
width: var(--livestream-comments-width);
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
padding-left: var(--spacing-xxs);
|
||||
border-radius: var(--border-radius);
|
||||
border: 1px solid var(--color-border);
|
||||
|
||||
.livestream__comment {
|
||||
overflow: unset;
|
||||
|
||||
.livestreamComment__body {
|
||||
margin: 0px;
|
||||
width: 100%;
|
||||
|
||||
.markdown-preview {
|
||||
p,
|
||||
.button__label {
|
||||
white-space: nowrap !important;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
a {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: var(--font-xxsmall) !important;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
padding: 0;
|
||||
padding-left: var(--spacing-xxs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.livestreamSuperchat__amount--large {
|
||||
|
@ -266,6 +316,10 @@ $recent-msg-button__height: 2rem;
|
|||
}
|
||||
&:nth-of-type(3) {
|
||||
background-color: var(--color-superchat-3);
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-of-type(-n + 3) {
|
||||
|
@ -280,6 +334,7 @@ $recent-msg-button__height: 2rem;
|
|||
}
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
background-color: #fff;
|
||||
padding: 5px;
|
||||
padding-bottom: 2px;
|
||||
|
||||
|
|
|
@ -30,6 +30,22 @@
|
|||
.channel-name {
|
||||
font-size: var(--font-xsmall);
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.livestreamComment__menu {
|
||||
position: relative;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
span,
|
||||
p {
|
||||
font-size: var(--font-xxsmall) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.livestream__comment--mentioned {
|
||||
|
@ -42,6 +58,7 @@
|
|||
|
||||
.livestream__comment--superchat {
|
||||
background-color: var(--color-card-background-highlighted);
|
||||
display: unset;
|
||||
|
||||
+ .livestream__comment--superchat {
|
||||
margin-bottom: var(--spacing-xxs);
|
||||
|
@ -58,6 +75,10 @@
|
|||
margin-top: calc(var(--spacing-xxs) / 2);
|
||||
}
|
||||
|
||||
.livestreamComment__menu {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
|
|
@ -87,11 +87,13 @@
|
|||
z-index: 0;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
min-height: calc(100vh - var(--header-height));
|
||||
|
||||
@media (min-width: $breakpoint-small) {
|
||||
min-height: calc(100vh - var(--header-height));
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
width: 100%;
|
||||
min-height: calc(100vh - var(--header-height-mobile));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,7 +239,7 @@
|
|||
}
|
||||
|
||||
.file-page__recommended {
|
||||
margin-top: var(--spacing-xxs) !important;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@
|
|||
margin: 0 !important;
|
||||
padding: var(--spacing-xxs);
|
||||
width: 3.1rem;
|
||||
height: 3.1rem;
|
||||
height: 3.5rem;
|
||||
|
||||
.button__content {
|
||||
flex-direction: column;
|
||||
|
|
|
@ -20,6 +20,31 @@
|
|||
left: calc(50% - 15px);
|
||||
}
|
||||
|
||||
.swipeable-drawer__header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: var(--spacing-s) var(--spacing-xxs);
|
||||
font-size: var(--font-small);
|
||||
}
|
||||
|
||||
.swipeable-drawer__title-menu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
margin-left: var(--spacing-xxs);
|
||||
}
|
||||
}
|
||||
|
||||
.swipeable-drawer__header-actions {
|
||||
display: flex;
|
||||
|
||||
button:not(:last-child) {
|
||||
margin-right: var(--spacing-xxs);
|
||||
}
|
||||
}
|
||||
|
||||
.swipeable-drawer__expand-button {
|
||||
width: 100%;
|
||||
margin: var(--spacing-xxs) 0;
|
||||
|
|
|
@ -123,3 +123,7 @@ export function getClaimTitle(claim: ?Claim) {
|
|||
const metadata = getClaimMetadata(claim);
|
||||
return metadata && metadata.title;
|
||||
}
|
||||
|
||||
export const isStreamPlaceholderClaim = (claim: ?StreamClaim) => {
|
||||
return claim ? Boolean(claim.value_type === 'stream' && !claim.value.source) : false;
|
||||
};
|
||||
|
|
|
@ -108,3 +108,12 @@ export function getStickerUrl(comment: string) {
|
|||
const stickerFromComment = parseSticker(comment);
|
||||
return stickerFromComment && stickerFromComment.url;
|
||||
}
|
||||
|
||||
export function getCommentsListTitle(totalComments: number) {
|
||||
const title =
|
||||
(totalComments === 0 && __('Leave a comment')) ||
|
||||
(totalComments === 1 && __('1 comment')) ||
|
||||
__('%total_comments% comments', { total_comments: totalComments });
|
||||
|
||||
return title;
|
||||
}
|
||||
|
|
|
@ -22,3 +22,24 @@ export function getLivestreamUris(activeLivestreams: ?LivestreamInfo, channelIds
|
|||
// $FlowFixMe
|
||||
return values.map((v) => v.claimUri);
|
||||
}
|
||||
|
||||
export function getTipValues(superChatsByAmount: Array<Comment>) {
|
||||
let superChatsChannelUrls = [];
|
||||
let superChatsFiatAmount = 0;
|
||||
let superChatsLBCAmount = 0;
|
||||
|
||||
if (superChatsByAmount) {
|
||||
superChatsByAmount.forEach((superChat) => {
|
||||
const { is_fiat: isFiat, support_amount: tipAmount, channel_url: uri } = superChat;
|
||||
|
||||
if (isFiat) {
|
||||
superChatsFiatAmount = superChatsFiatAmount + tipAmount;
|
||||
} else {
|
||||
superChatsLBCAmount = superChatsLBCAmount + tipAmount;
|
||||
}
|
||||
superChatsChannelUrls.push(uri || '0');
|
||||
});
|
||||
}
|
||||
|
||||
return { superChatsChannelUrls, superChatsFiatAmount, superChatsLBCAmount };
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue