mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 12:56:41 +07:00
play video in embed instead of new window on desktops
This commit is contained in:
parent
5056047546
commit
06cf7050a9
8 changed files with 184 additions and 44 deletions
|
@ -0,0 +1,28 @@
|
|||
import classNames from 'classnames';
|
||||
import { useResizeDetector } from 'react-resize-detector';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
title: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const CommentVideoFrame = ({ id, title, className }: Props) => {
|
||||
const { ref, width = 0, height = 0 } = useResizeDetector();
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.wrap, className)} ref={ref}>
|
||||
<iframe
|
||||
width={width}
|
||||
height={height}
|
||||
src={`https://www.youtube.com/embed/${id}?autoplay=1`}
|
||||
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
|
||||
frameBorder="0"
|
||||
allowFullScreen
|
||||
title={title}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
@import '~/styles/variables';
|
||||
|
||||
.wrap {
|
||||
width: 100%;
|
||||
aspect-ratio: calc(16 / 9);
|
||||
overflow: hidden;
|
||||
border-radius: $radius;
|
||||
}
|
|
@ -1,15 +1,21 @@
|
|||
import { FC, memo, useMemo } from 'react';
|
||||
import { FC, memo, useCallback, useMemo } from 'react';
|
||||
|
||||
import { Icon } from '~/components/common/Icon';
|
||||
import { ICommentBlockProps } from '~/constants/comment';
|
||||
import { useWindowSize } from '~/hooks/dom/useWindowSize';
|
||||
import { useYoutubeMetadata } from '~/hooks/metadata/useYoutubeMetadata';
|
||||
import { getYoutubeThumb } from '~/utils/dom';
|
||||
import { useVideoPlayer } from '~/utils/providers/VideoPlayerProvider';
|
||||
|
||||
import { CommentVideoFrame } from './components/CommentVideoFrame';
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
type Props = ICommentBlockProps & {};
|
||||
|
||||
const CommentEmbedBlock: FC<Props> = memo(({ block }) => {
|
||||
const { isTablet } = useWindowSize();
|
||||
const { url, setUrl } = useVideoPlayer();
|
||||
|
||||
const id = useMemo(() => {
|
||||
const match = block.content.match(
|
||||
/https?:\/\/(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch)?(?:\?v=)?([\w\-=]+)/,
|
||||
|
@ -18,7 +24,7 @@ const CommentEmbedBlock: FC<Props> = memo(({ block }) => {
|
|||
return (match && match[1]) || '';
|
||||
}, [block.content]);
|
||||
|
||||
const url = useMemo(() => `https://youtube.com/watch?v=${id}`, [id]);
|
||||
const address = `https://youtube.com/watch?v=${id}`;
|
||||
|
||||
const preview = useMemo(
|
||||
() => getYoutubeThumb(block.content),
|
||||
|
@ -28,21 +34,46 @@ const CommentEmbedBlock: FC<Props> = memo(({ block }) => {
|
|||
const metadata = useYoutubeMetadata(id);
|
||||
const title = metadata?.metadata?.title || '';
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
if (isTablet) {
|
||||
window.open(address, '_blank');
|
||||
return;
|
||||
}
|
||||
|
||||
setUrl(address);
|
||||
}, [isTablet, setUrl, address]);
|
||||
|
||||
const closeVideo = useCallback(() => setUrl(''), [setUrl]);
|
||||
|
||||
return (
|
||||
<div className={styles.embed}>
|
||||
<a href={url} target="_blank" rel="noreferrer" />
|
||||
|
||||
<div className={styles.preview}>
|
||||
<div style={{ backgroundImage: `url("${preview}")` }}>
|
||||
<div className={styles.backdrop}>
|
||||
<div className={styles.play}>
|
||||
<Icon icon="play" size={32} />
|
||||
</div>
|
||||
|
||||
<div className={styles.title}>{title}</div>
|
||||
{url === address ? (
|
||||
<div className={styles.video}>
|
||||
<div className={styles.close} onClick={closeVideo}>
|
||||
<Icon icon="close" />
|
||||
</div>
|
||||
<div className={styles.animation}>
|
||||
<CommentVideoFrame id={id} title={title} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={styles.preview}
|
||||
role="button"
|
||||
onClick={onClick}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<div style={{ backgroundImage: `url("${preview}")` }}>
|
||||
<div className={styles.backdrop}>
|
||||
<div className={styles.play}>
|
||||
<Icon icon="play" size={32} />
|
||||
</div>
|
||||
|
||||
<div className={styles.title}>{title}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
@import 'src/styles/variables';
|
||||
|
||||
.embed {
|
||||
padding: 0 $gap;
|
||||
height: $comment_height;
|
||||
padding: 0 0;
|
||||
min-height: $comment_height;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: 50% 50% no-repeat;
|
||||
|
@ -69,6 +69,7 @@
|
|||
justify-content: stretch;
|
||||
box-sizing: border-box;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
|
@ -98,3 +99,47 @@
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
0% {
|
||||
grid-template-columns: 0fr;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
50% {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.video {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
padding: $gap / 2;
|
||||
}
|
||||
|
||||
.close {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--color_danger);
|
||||
width: 64px;
|
||||
height: 24px;
|
||||
position: absolute;
|
||||
bottom: calc(100% - #{$gap / 2});
|
||||
right: 24px;
|
||||
border-radius: $radius $radius 0 0;
|
||||
z-index: 10;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.animation {
|
||||
background-color: var(--content_bg_darker);
|
||||
display: grid;
|
||||
animation: appear 0.5s forwards;
|
||||
width: 100%;
|
||||
border-radius: $radius;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import { useCommentContext } from '~/utils/context/CommentContextProvider';
|
|||
import { useNodeContext } from '~/utils/context/NodeContextProvider';
|
||||
import { useUserContext } from '~/utils/context/UserContextProvider';
|
||||
import { canEditComment, canLikeComment } from '~/utils/node';
|
||||
import { VideoPlayerProvider } from '~/utils/providers/VideoPlayerProvider';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
|
@ -84,38 +85,40 @@ const NodeComments: FC<Props> = observer(({ order }) => {
|
|||
}, [isLoading]);
|
||||
|
||||
return (
|
||||
<div className={styles.wrap}>
|
||||
{order === 'DESC' && more}
|
||||
<VideoPlayerProvider>
|
||||
<div className={styles.wrap}>
|
||||
{order === 'DESC' && more}
|
||||
|
||||
{groupped.map((group, index) => (
|
||||
<>
|
||||
{isFirstGroupWithNewComment(group, groupped[index - 1]) && (
|
||||
<a
|
||||
id={NEW_COMMENT_ANCHOR_NAME}
|
||||
className={styles.newCommentAnchor}
|
||||
{groupped.map((group, index) => (
|
||||
<>
|
||||
{isFirstGroupWithNewComment(group, groupped[index - 1]) && (
|
||||
<a
|
||||
id={NEW_COMMENT_ANCHOR_NAME}
|
||||
className={styles.newCommentAnchor}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Comment
|
||||
nodeId={node.id!}
|
||||
key={group.ids.join()}
|
||||
group={group}
|
||||
highlighted={
|
||||
node.id === BORIS_NODE_ID && group.user.id === ANNOUNCE_USER_ID
|
||||
}
|
||||
onLike={onLike}
|
||||
canLike={canLikeComment(group, user)}
|
||||
canEdit={canEditComment(group, user)}
|
||||
onDelete={onDeleteComment}
|
||||
onShowImageModal={onShowImageModal}
|
||||
isSame={group.user.id === user.id}
|
||||
saveComment={onSaveComment}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
|
||||
<Comment
|
||||
nodeId={node.id!}
|
||||
key={group.ids.join()}
|
||||
group={group}
|
||||
highlighted={
|
||||
node.id === BORIS_NODE_ID && group.user.id === ANNOUNCE_USER_ID
|
||||
}
|
||||
onLike={onLike}
|
||||
canLike={canLikeComment(group, user)}
|
||||
canEdit={canEditComment(group, user)}
|
||||
onDelete={onDeleteComment}
|
||||
onShowImageModal={onShowImageModal}
|
||||
isSame={group.user.id === user.id}
|
||||
saveComment={onSaveComment}
|
||||
/>
|
||||
</>
|
||||
))}
|
||||
|
||||
{order === 'ASC' && more}
|
||||
</div>
|
||||
{order === 'ASC' && more}
|
||||
</div>
|
||||
</VideoPlayerProvider>
|
||||
);
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue