mirror of
https://github.com/Qortal/q-tube.git
synced 2025-02-11 17:55:51 +00:00
Merge pull request #54 from QortalSeth/main
Mobile support for Home, Video, and Channel pages.
This commit is contained in:
commit
29de8a8a9f
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "qtube",
|
"name": "qtube",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { smallScreenSizeString } from "../../constants/Misc.ts";
|
||||||
import { CardContentContainerComment } from "../common/Comments/Comments-styles";
|
import { CardContentContainerComment } from "../common/Comments/Comments-styles";
|
||||||
import {
|
import {
|
||||||
CrowdfundSubTitle,
|
CrowdfundSubTitle,
|
||||||
CrowdfundSubTitleRow,
|
CrowdfundSubTitleRow,
|
||||||
} from "../Publish/PublishVideo/PublishVideo-styles.tsx";
|
} from "../Publish/PublishVideo/PublishVideo-styles.tsx";
|
||||||
import { Box, Typography, useTheme } from "@mui/material";
|
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
export const Playlists = ({
|
export const Playlists = ({
|
||||||
@ -13,7 +14,8 @@ export const Playlists = ({
|
|||||||
onClick,
|
onClick,
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const navigate = useNavigate();
|
const isScreenSmall = !useMediaQuery(`(min-width:700px)`);
|
||||||
|
const videoPlayerHeight = "33.75vw"; // This is videoplayer width * 9/16 (inverse of aspect ratio)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -21,7 +23,7 @@ export const Playlists = ({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
maxHeight: "30.94vw",
|
height: isScreenSmall ? "200px" : videoPlayerHeight,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CardContentContainerComment
|
<CardContentContainerComment
|
||||||
@ -32,7 +34,7 @@ export const Playlists = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{playlistData?.videos?.map((vid, index) => {
|
{playlistData?.videos?.map((vid, index) => {
|
||||||
const isCurrentVidPlayling =
|
const isCurrentVidPlaying =
|
||||||
vid?.identifier === currentVideoIdentifier;
|
vid?.identifier === currentVideoIdentifier;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -42,15 +44,15 @@ export const Playlists = ({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
gap: "10px",
|
gap: "10px",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
background: isCurrentVidPlayling && theme.palette.primary.main,
|
background: isCurrentVidPlaying && theme.palette.primary.main,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
padding: "10px",
|
padding: "10px",
|
||||||
borderRadius: "5px",
|
borderRadius: "5px",
|
||||||
cursor: isCurrentVidPlayling ? "default" : "pointer",
|
cursor: isCurrentVidPlaying ? "default" : "pointer",
|
||||||
userSelect: "none",
|
userSelect: "none",
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (isCurrentVidPlayling) return;
|
if (isCurrentVidPlaying) return;
|
||||||
onClick(vid.name, vid.identifier);
|
onClick(vid.name, vid.identifier);
|
||||||
// navigate(`/video/${vid.name}/${vid.identifier}`)
|
// navigate(`/video/${vid.name}/${vid.identifier}`)
|
||||||
}}
|
}}
|
||||||
|
@ -15,8 +15,10 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
import { useSignal } from "@preact/signals-react";
|
||||||
|
import { useSignals } from "@preact/signals-react/runtime";
|
||||||
import Compressor from "compressorjs";
|
import Compressor from "compressorjs";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useDropzone } from "react-dropzone";
|
import { useDropzone } from "react-dropzone";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
@ -27,6 +29,7 @@ import {
|
|||||||
} from "../../../constants/Identifiers.ts";
|
} from "../../../constants/Identifiers.ts";
|
||||||
import {
|
import {
|
||||||
maxSize,
|
maxSize,
|
||||||
|
menuIconSize,
|
||||||
titleFormatter,
|
titleFormatter,
|
||||||
videoMaxSize,
|
videoMaxSize,
|
||||||
} from "../../../constants/Misc.ts";
|
} from "../../../constants/Misc.ts";
|
||||||
@ -38,7 +41,6 @@ import {
|
|||||||
|
|
||||||
import { setNotification } from "../../../state/features/notificationsSlice.ts";
|
import { setNotification } from "../../../state/features/notificationsSlice.ts";
|
||||||
import { RootState } from "../../../state/store.ts";
|
import { RootState } from "../../../state/store.ts";
|
||||||
import { formatBytes } from "../../../utils/numberFunctions.ts";
|
|
||||||
import { objectToBase64 } from "../../../utils/PublishFormatter.ts";
|
import { objectToBase64 } from "../../../utils/PublishFormatter.ts";
|
||||||
import { getFileName } from "../../../utils/stringFunctions.ts";
|
import { getFileName } from "../../../utils/stringFunctions.ts";
|
||||||
import { CardContentContainerComment } from "../../common/Comments/Comments-styles.tsx";
|
import { CardContentContainerComment } from "../../common/Comments/Comments-styles.tsx";
|
||||||
@ -66,8 +68,7 @@ import {
|
|||||||
StyledButton,
|
StyledButton,
|
||||||
TimesIcon,
|
TimesIcon,
|
||||||
} from "./PublishVideo-styles.tsx";
|
} from "./PublishVideo-styles.tsx";
|
||||||
import { signal, Signal, useSignal } from "@preact/signals-react";
|
import VideoLibraryIcon from "@mui/icons-material/VideoLibrary";
|
||||||
import { useSignals } from "@preact/signals-react/runtime";
|
|
||||||
|
|
||||||
export const toBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
|
export const toBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
@ -82,13 +83,14 @@ export const toBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
|
|||||||
const uid = new ShortUniqueId();
|
const uid = new ShortUniqueId();
|
||||||
const shortuid = new ShortUniqueId({ length: 5 });
|
const shortuid = new ShortUniqueId({ length: 5 });
|
||||||
|
|
||||||
interface NewCrowdfundProps {
|
interface PublishVideoProps {
|
||||||
editId?: string;
|
editId?: string;
|
||||||
editContent?: null | {
|
editContent?: null | {
|
||||||
title: string;
|
title: string;
|
||||||
user: string;
|
user: string;
|
||||||
coverImage: string | null;
|
coverImage: string | null;
|
||||||
};
|
};
|
||||||
|
afterClose?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface VideoFile {
|
interface VideoFile {
|
||||||
@ -97,7 +99,11 @@ interface VideoFile {
|
|||||||
description: string;
|
description: string;
|
||||||
coverImage?: string;
|
coverImage?: string;
|
||||||
}
|
}
|
||||||
export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
|
export const PublishVideo = ({
|
||||||
|
editId,
|
||||||
|
editContent,
|
||||||
|
afterClose,
|
||||||
|
}: PublishVideoProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [isOpenMultiplePublish, setIsOpenMultiplePublish] = useState(false);
|
const [isOpenMultiplePublish, setIsOpenMultiplePublish] = useState(false);
|
||||||
@ -188,13 +194,9 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// if (editContent) {
|
|
||||||
// }
|
|
||||||
// }, [editContent]);
|
|
||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
|
if (afterClose) afterClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const search = async () => {
|
const search = async () => {
|
||||||
@ -633,12 +635,20 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
|
|||||||
{editId ? null : (
|
{editId ? null : (
|
||||||
<StyledButton
|
<StyledButton
|
||||||
color="primary"
|
color="primary"
|
||||||
startIcon={<AddBoxIcon />}
|
startIcon={
|
||||||
|
<VideoLibraryIcon
|
||||||
|
sx={{
|
||||||
|
color: "#FF0033",
|
||||||
|
width: menuIconSize,
|
||||||
|
height: menuIconSize,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
add video
|
Video
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -159,7 +159,7 @@ export const BlockIconContainer = styled(Box)({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const CommentsContainer = styled(Box)({
|
export const CommentsContainer = styled(Box)({
|
||||||
width: "70%",
|
width: "90%",
|
||||||
maxWidth: "1000px",
|
maxWidth: "1000px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
|
@ -160,10 +160,10 @@ export const LikeAndDislike = ({ name, identifier }: LikeAndDislikeProps) => {
|
|||||||
sx={{
|
sx={{
|
||||||
padding: "5px",
|
padding: "5px",
|
||||||
borderRadius: "7px",
|
borderRadius: "7px",
|
||||||
gap: "5px",
|
gap: "10px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
marginRight: "10px",
|
marginRight: "20px",
|
||||||
height: "53px",
|
height: "53px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -1,32 +1,29 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import ThumbUpIcon from "@mui/icons-material/ThumbUp";
|
import ThumbUpIcon from "@mui/icons-material/ThumbUp";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
|
||||||
Dialog,
|
|
||||||
DialogActions,
|
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
|
||||||
FormControl,
|
|
||||||
Input,
|
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
InputLabel,
|
InputLabel,
|
||||||
MenuItem,
|
|
||||||
Modal,
|
Modal,
|
||||||
Select,
|
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import qortImg from "../../../assets/img/qort.png";
|
import React, { useEffect, useState } from "react";
|
||||||
import { MultiplePublish } from "../../Publish/MultiplePublish/MultiplePublishAll.tsx";
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { setNotification } from "../../../state/features/notificationsSlice.ts";
|
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
|
import qortImg from "../../../assets/img/qort.png";
|
||||||
import {
|
import {
|
||||||
objectToBase64,
|
FOR,
|
||||||
objectToFile,
|
FOR_SUPER_LIKE,
|
||||||
} from "../../../utils/PublishFormatter.ts";
|
SUPER_LIKE_BASE,
|
||||||
|
} from "../../../constants/Identifiers.ts";
|
||||||
import { minPriceSuperlike } from "../../../constants/Misc.ts";
|
import { minPriceSuperlike } from "../../../constants/Misc.ts";
|
||||||
import { CommentInput } from "../Comments/Comments-styles.tsx";
|
import { setNotification } from "../../../state/features/notificationsSlice.ts";
|
||||||
|
import { RootState } from "../../../state/store.ts";
|
||||||
|
import BoundedNumericTextField from "../../../utils/BoundedNumericTextField.tsx";
|
||||||
|
import { numberToInt, truncateNumber } from "../../../utils/numberFunctions.ts";
|
||||||
|
import { objectToBase64 } from "../../../utils/PublishFormatter.ts";
|
||||||
|
import { getUserBalance } from "../../../utils/qortalRequestFunctions.ts";
|
||||||
|
import { MultiplePublish } from "../../Publish/MultiplePublish/MultiplePublishAll.tsx";
|
||||||
import {
|
import {
|
||||||
CrowdfundActionButton,
|
CrowdfundActionButton,
|
||||||
CrowdfundActionButtonRow,
|
CrowdfundActionButtonRow,
|
||||||
@ -34,16 +31,7 @@ import {
|
|||||||
NewCrowdfundTitle,
|
NewCrowdfundTitle,
|
||||||
Spacer,
|
Spacer,
|
||||||
} from "../../Publish/PublishVideo/PublishVideo-styles.tsx";
|
} from "../../Publish/PublishVideo/PublishVideo-styles.tsx";
|
||||||
import { utf8ToBase64 } from "../SuperLikesList/CommentEditor.tsx";
|
import { CommentInput } from "../Comments/Comments-styles.tsx";
|
||||||
import { RootState } from "../../../state/store.ts";
|
|
||||||
import {
|
|
||||||
FOR,
|
|
||||||
FOR_SUPER_LIKE,
|
|
||||||
SUPER_LIKE_BASE,
|
|
||||||
} from "../../../constants/Identifiers.ts";
|
|
||||||
import BoundedNumericTextField from "../../../utils/BoundedNumericTextField.tsx";
|
|
||||||
import { numberToInt, truncateNumber } from "../../../utils/numberFunctions.ts";
|
|
||||||
import { getUserBalance } from "../../../utils/qortalRequestFunctions.ts";
|
|
||||||
|
|
||||||
const uid = new ShortUniqueId({ length: 4 });
|
const uid = new ShortUniqueId({ length: 4 });
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect } from "react";
|
||||||
import {
|
import {
|
||||||
Accordion,
|
Accordion,
|
||||||
AccordionDetails,
|
AccordionDetails,
|
||||||
@ -11,29 +11,28 @@ import {
|
|||||||
ListItemIcon,
|
ListItemIcon,
|
||||||
Popover,
|
Popover,
|
||||||
Typography,
|
Typography,
|
||||||
useTheme
|
useTheme,
|
||||||
} from '@mui/material'
|
} from "@mui/material";
|
||||||
import { Movie } from '@mui/icons-material'
|
import { Movie } from "@mui/icons-material";
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from "react-redux";
|
||||||
import { RootState } from '../../state/store'
|
import { RootState } from "../../state/store";
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import { DownloadingLight } from '../../assets/svgs/DownloadingLight'
|
import { DownloadingLight } from "../../assets/svgs/DownloadingLight";
|
||||||
import { DownloadedLight } from '../../assets/svgs/DownloadedLight'
|
import { DownloadedLight } from "../../assets/svgs/DownloadedLight";
|
||||||
|
|
||||||
export const DownloadTaskManager: React.FC = () => {
|
export const DownloadTaskManager: React.FC = () => {
|
||||||
const { downloads } = useSelector((state: RootState) => state.global)
|
const { downloads } = useSelector((state: RootState) => state.global);
|
||||||
const location = useLocation()
|
const theme = useTheme();
|
||||||
const theme = useTheme()
|
const [visible, setVisible] = useState(false);
|
||||||
const [visible, setVisible] = useState(false)
|
const [hidden, setHidden] = useState(true);
|
||||||
const [hidden, setHidden] = useState(true)
|
const navigate = useNavigate();
|
||||||
const navigate = useNavigate()
|
|
||||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||||
|
|
||||||
|
|
||||||
const [openDownload, setOpenDownload] = useState<boolean>(false);
|
const [openDownload, setOpenDownload] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const hashMapVideos = useSelector(
|
||||||
|
(state: RootState) => state.video.hashMapVideos
|
||||||
|
);
|
||||||
const handleClick = (event?: React.MouseEvent<HTMLDivElement>) => {
|
const handleClick = (event?: React.MouseEvent<HTMLDivElement>) => {
|
||||||
const target = event?.currentTarget as unknown as HTMLButtonElement | null;
|
const target = event?.currentTarget as unknown as HTMLButtonElement | null;
|
||||||
setAnchorEl(target);
|
setAnchorEl(target);
|
||||||
@ -49,155 +48,159 @@ export const DownloadTaskManager: React.FC = () => {
|
|||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setHidden(true)
|
setHidden(true);
|
||||||
setVisible(false)
|
setVisible(false);
|
||||||
}, 3000)
|
}, 3000);
|
||||||
}
|
}
|
||||||
}, [visible])
|
}, [visible]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Object.keys(downloads).length === 0) return
|
if (Object.keys(downloads).length === 0) return;
|
||||||
setVisible(true)
|
setVisible(true);
|
||||||
setHidden(false)
|
setHidden(false);
|
||||||
}, [downloads])
|
}, [downloads]);
|
||||||
|
|
||||||
|
if (!downloads || Object.keys(downloads).length === 0) return null;
|
||||||
|
|
||||||
|
let downloadInProgress = false;
|
||||||
if (
|
if (
|
||||||
!downloads ||
|
Object.keys(downloads).find(
|
||||||
Object.keys(downloads).length === 0
|
key =>
|
||||||
)
|
downloads[key]?.status?.status !== "READY" &&
|
||||||
return null
|
downloads[key]?.status?.status !== "DOWNLOADED"
|
||||||
|
)
|
||||||
|
) {
|
||||||
let downloadInProgress = false
|
downloadInProgress = true;
|
||||||
if(Object.keys(downloads).find((key)=> (downloads[key]?.status?.status !== 'READY' && downloads[key]?.status?.status !== 'DOWNLOADED'))){
|
|
||||||
downloadInProgress = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Button onClick={(e: any) => {
|
<Button
|
||||||
handleClick(e);
|
sx={{ padding: "0px 0px", minWidth: "0px" }}
|
||||||
setOpenDownload(true);
|
onClick={(e: any) => {
|
||||||
}}>
|
handleClick(e);
|
||||||
{downloadInProgress ? (
|
setOpenDownload(true);
|
||||||
<DownloadingLight height='24px' width='24px' className='download-icon' />
|
}}
|
||||||
) : (
|
>
|
||||||
<DownloadedLight height='24px' width='24px' />
|
{downloadInProgress ? (
|
||||||
)}
|
<DownloadingLight
|
||||||
|
height="24px"
|
||||||
</Button>
|
width="24px"
|
||||||
|
className="download-icon"
|
||||||
<Popover
|
/>
|
||||||
id={"download-popover"}
|
) : (
|
||||||
open={openDownload}
|
<DownloadedLight height="24px" width="24px" />
|
||||||
anchorEl={anchorEl}
|
)}
|
||||||
onClose={handleCloseDownload}
|
</Button>
|
||||||
anchorOrigin={{
|
|
||||||
vertical: "bottom",
|
<Popover
|
||||||
horizontal: "left"
|
id={"download-popover"}
|
||||||
|
open={openDownload}
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
onClose={handleCloseDownload}
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: "bottom",
|
||||||
|
horizontal: "left",
|
||||||
|
}}
|
||||||
|
sx={{ marginTop: "12px" }}
|
||||||
|
>
|
||||||
|
<List
|
||||||
|
sx={{
|
||||||
|
maxHeight: "50vh",
|
||||||
|
overflow: "auto",
|
||||||
|
width: "100%",
|
||||||
|
maxWidth: "400px",
|
||||||
|
gap: "5px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
backgroundColor: "#555555",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<List
|
{Object.keys(downloads).map((download: any) => {
|
||||||
sx={{
|
const downloadObj = downloads[download];
|
||||||
maxHeight: '50vh',
|
const progress = downloads[download]?.status?.percentLoaded || 0;
|
||||||
overflow: 'auto',
|
const status = downloads[download]?.status?.status;
|
||||||
width: '250px',
|
const service = downloads[download]?.service;
|
||||||
gap: '5px',
|
const id =
|
||||||
display: 'flex',
|
downloadObj?.identifier + "_metadata-" + downloadObj?.name;
|
||||||
flexDirection: 'column',
|
const videoTitle = hashMapVideos[id]?.title;
|
||||||
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{Object.keys(downloads)
|
|
||||||
.map((download: any) => {
|
|
||||||
const downloadObj = downloads[download]
|
|
||||||
const progress = downloads[download]?.status?.percentLoaded || 0
|
|
||||||
const status = downloads[download]?.status?.status
|
|
||||||
const service = downloads[download]?.service
|
|
||||||
return (
|
|
||||||
<ListItem
|
|
||||||
key={downloadObj?.identifier}
|
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
width: '100%',
|
|
||||||
justifyContent: 'center',
|
|
||||||
background: theme.palette.primary.main,
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
cursor: 'pointer',
|
|
||||||
padding: '2px',
|
|
||||||
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
const id = downloadObj?.properties?.jsonId
|
|
||||||
if (!id) return
|
|
||||||
|
|
||||||
navigate(
|
|
||||||
`/video/${downloadObj?.properties?.user}/${id}`
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ListItemIcon>
|
|
||||||
{service === 'VIDEO' && (
|
|
||||||
<Movie sx={{ color: theme.palette.text.primary }} />
|
|
||||||
)}
|
|
||||||
</ListItemIcon>
|
|
||||||
|
|
||||||
<Box
|
return (
|
||||||
sx={{ width: '100px', marginLeft: 1, marginRight: 1 }}
|
<ListItem
|
||||||
>
|
key={downloadObj?.identifier}
|
||||||
<LinearProgress
|
sx={{
|
||||||
variant="determinate"
|
display: "flex",
|
||||||
value={progress}
|
flexDirection: "column",
|
||||||
sx={{
|
width: "100%",
|
||||||
borderRadius: '5px',
|
justifyContent: "center",
|
||||||
color: theme.palette.secondary.main
|
background: theme.palette.primary.main,
|
||||||
}}
|
color: theme.palette.text.primary,
|
||||||
/>
|
cursor: "pointer",
|
||||||
</Box>
|
padding: "2px",
|
||||||
<Typography
|
}}
|
||||||
sx={{
|
onClick={() => {
|
||||||
fontFamily: 'Arial',
|
const userName = downloadObj?.name;
|
||||||
color: theme.palette.text.primary
|
const identifier = downloadObj?.identifier;
|
||||||
}}
|
|
||||||
variant="caption"
|
if (identifier && userName)
|
||||||
>
|
navigate(`/video/${userName}/${identifier}_metadata`);
|
||||||
{`${progress?.toFixed(0)}%`}{' '}
|
}}
|
||||||
{status && status === 'REFETCHING' && '- refetching'}
|
>
|
||||||
{status && status === 'DOWNLOADED' && '- building'}
|
<Box
|
||||||
</Typography>
|
sx={{
|
||||||
</Box>
|
width: "100%",
|
||||||
<Typography
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ListItemIcon>
|
||||||
|
{service === "VIDEO" && (
|
||||||
|
<Movie sx={{ color: theme.palette.text.primary }} />
|
||||||
|
)}
|
||||||
|
</ListItemIcon>
|
||||||
|
|
||||||
|
<Box sx={{ width: "100px", marginLeft: 1, marginRight: 1 }}>
|
||||||
|
<LinearProgress
|
||||||
|
variant="determinate"
|
||||||
|
value={progress}
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: '10px',
|
borderRadius: "5px",
|
||||||
width: '100%',
|
color: theme.palette.secondary.main,
|
||||||
textAlign: 'end',
|
|
||||||
fontFamily: 'Arial',
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
overflow: 'hidden',
|
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
{downloadObj?.identifier}
|
</Box>
|
||||||
</Typography>
|
<Typography
|
||||||
</ListItem>
|
sx={{
|
||||||
)
|
fontFamily: "Arial",
|
||||||
})}
|
color: theme.palette.text.primary,
|
||||||
</List>
|
}}
|
||||||
</Popover>
|
variant="caption"
|
||||||
|
>
|
||||||
|
{`${progress?.toFixed(0)}%`}{" "}
|
||||||
|
{status && status === "REFETCHING" && "- refetching"}
|
||||||
|
{status && status === "DOWNLOADED" && "- building"}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "10px",
|
||||||
|
width: "100%",
|
||||||
|
textAlign: "start",
|
||||||
|
fontFamily: "Arial",
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{videoTitle || downloadObj?.identifier}
|
||||||
|
</Typography>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
</Popover>
|
||||||
</Box>
|
</Box>
|
||||||
|
);
|
||||||
)
|
};
|
||||||
}
|
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
import { Box, Typography } from "@mui/material";
|
import { Box, Tooltip, Typography, useMediaQuery } from "@mui/material";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { PopMenu } from "../PopMenu.tsx";
|
||||||
import ListSuperLikes from "./ListSuperLikes";
|
import ListSuperLikes from "./ListSuperLikes";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { RootState } from "../../../state/store";
|
import { RootState } from "../../../state/store";
|
||||||
|
import ThumbUpIcon from "@mui/icons-material/ThumbUp";
|
||||||
export const ListSuperLikeContainer = () => {
|
export const ListSuperLikeContainer = () => {
|
||||||
const superlikelist = useSelector(
|
const superlikelist = useSelector(
|
||||||
(state: RootState) => state.global.superlikelistAll
|
(state: RootState) => state.global.superlikelistAll
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
const isScreenLarge = useMediaQuery("(min-width:1200px)");
|
||||||
<Box>
|
const superlikeListComponent = (
|
||||||
|
<>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "18px",
|
fontSize: "18px",
|
||||||
@ -20,6 +22,53 @@ export const ListSuperLikeContainer = () => {
|
|||||||
Recent Super likes
|
Recent Super likes
|
||||||
</Typography>
|
</Typography>
|
||||||
<ListSuperLikes superlikes={superlikelist} />
|
<ListSuperLikes superlikes={superlikelist} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
return (
|
||||||
|
<Box sx={{ paddingLeft: "5px" }}>
|
||||||
|
{isScreenLarge ? (
|
||||||
|
<>{superlikeListComponent}</>
|
||||||
|
) : (
|
||||||
|
<PopMenu
|
||||||
|
showExpandIcon={false}
|
||||||
|
popoverProps={{
|
||||||
|
open: undefined,
|
||||||
|
sx: {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
anchorReference: "none",
|
||||||
|
}}
|
||||||
|
MenuHeader={
|
||||||
|
<Tooltip title={"Show recent Superlikes"} placement={"left"} arrow>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
padding: "5px",
|
||||||
|
borderRadius: "7px",
|
||||||
|
outline: "1px gold solid",
|
||||||
|
height: "53px",
|
||||||
|
position: "absolute",
|
||||||
|
top: "60px",
|
||||||
|
right: "2%",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ThumbUpIcon
|
||||||
|
style={{
|
||||||
|
color: "gold",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{superlikeListComponent}
|
||||||
|
</PopMenu>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -64,7 +64,6 @@ export function extractIdValue(metadescription) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Notifications = () => {
|
export const Notifications = () => {
|
||||||
const dispatch = useDispatch();
|
|
||||||
const [anchorElNotification, setAnchorElNotification] =
|
const [anchorElNotification, setAnchorElNotification] =
|
||||||
useState<HTMLButtonElement | null>(null);
|
useState<HTMLButtonElement | null>(null);
|
||||||
const [notifications, setNotifications] = useState<any[]>([]);
|
const [notifications, setNotifications] = useState<any[]>([]);
|
||||||
@ -238,7 +237,7 @@ export const Notifications = () => {
|
|||||||
badgeContent={notificationBadgeLength}
|
badgeContent={notificationBadgeLength}
|
||||||
color="primary"
|
color="primary"
|
||||||
sx={{
|
sx={{
|
||||||
margin: "0px 12px",
|
margin: "0px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
@ -267,6 +266,7 @@ export const Notifications = () => {
|
|||||||
vertical: "bottom",
|
vertical: "bottom",
|
||||||
horizontal: "left",
|
horizontal: "left",
|
||||||
}}
|
}}
|
||||||
|
sx={{ marginTop: "12px" }}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<List
|
<List
|
||||||
|
77
src/components/common/PopMenu.tsx
Normal file
77
src/components/common/PopMenu.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||||
|
import { Box, Popover, PopoverProps, SxProps, Theme } from "@mui/material";
|
||||||
|
import {
|
||||||
|
forwardRef,
|
||||||
|
PropsWithChildren,
|
||||||
|
useImperativeHandle,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { AvatarContainer } from "../layout/Navbar/Navbar-styles.tsx";
|
||||||
|
|
||||||
|
export interface PopMenuProps {
|
||||||
|
MenuHeader: React.ReactNode;
|
||||||
|
containerSX?: SxProps<Theme>;
|
||||||
|
showExpandIcon?: boolean;
|
||||||
|
popoverProps?: PopoverProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PopMenuRefType = {
|
||||||
|
closePopover: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PopMenu = forwardRef<
|
||||||
|
PopMenuRefType,
|
||||||
|
PropsWithChildren<PopMenuProps>
|
||||||
|
>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
containerSX,
|
||||||
|
popoverProps,
|
||||||
|
MenuHeader,
|
||||||
|
showExpandIcon = true,
|
||||||
|
children,
|
||||||
|
}: PropsWithChildren<PopMenuProps>,
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
const [isOpenUserDropdown, setIsOpenUserDropdown] =
|
||||||
|
useState<boolean>(false);
|
||||||
|
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||||
|
|
||||||
|
const openPopover = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
const target = event.currentTarget as unknown as HTMLButtonElement | null;
|
||||||
|
|
||||||
|
setAnchorEl(target);
|
||||||
|
setIsOpenUserDropdown(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closePopover = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
setIsOpenUserDropdown(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
closePopover: () => closePopover(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AvatarContainer sx={containerSX} onClick={openPopover}>
|
||||||
|
{MenuHeader}
|
||||||
|
{showExpandIcon && <ExpandMoreIcon sx={{ color: "#ACB6BF" }} />}
|
||||||
|
</AvatarContainer>
|
||||||
|
<Popover
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: "bottom",
|
||||||
|
horizontal: "left",
|
||||||
|
}}
|
||||||
|
{...popoverProps}
|
||||||
|
open={isOpenUserDropdown}
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
onClose={closePopover}
|
||||||
|
>
|
||||||
|
<Box sx={{ display: "contents" }}>{children}</Box>
|
||||||
|
</Popover>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
@ -7,10 +7,15 @@ import {
|
|||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
Typography,
|
Typography,
|
||||||
|
useMediaQuery,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import React, { useCallback, useState, useEffect } from "react";
|
import React, { useCallback, useState, useEffect } from "react";
|
||||||
import ThumbUpIcon from "@mui/icons-material/ThumbUp";
|
import ThumbUpIcon from "@mui/icons-material/ThumbUp";
|
||||||
|
import {
|
||||||
|
fontSizeSmall,
|
||||||
|
smallScreenSizeString,
|
||||||
|
} from "../../../constants/Misc.ts";
|
||||||
|
|
||||||
import { CommentEditor } from "./CommentEditor";
|
import { CommentEditor } from "./CommentEditor";
|
||||||
import {
|
import {
|
||||||
@ -36,9 +41,9 @@ interface CommentProps {
|
|||||||
postId: string;
|
postId: string;
|
||||||
postName: string;
|
postName: string;
|
||||||
onSubmit: (obj?: any, isEdit?: boolean) => void;
|
onSubmit: (obj?: any, isEdit?: boolean) => void;
|
||||||
amount?: null | number
|
amount?: null | number;
|
||||||
isSuperLike?: boolean
|
isSuperLike?: boolean;
|
||||||
hasHash?: boolean
|
hasHash?: boolean;
|
||||||
}
|
}
|
||||||
export const Comment = ({
|
export const Comment = ({
|
||||||
comment,
|
comment,
|
||||||
@ -47,7 +52,7 @@ export const Comment = ({
|
|||||||
onSubmit,
|
onSubmit,
|
||||||
amount,
|
amount,
|
||||||
isSuperLike,
|
isSuperLike,
|
||||||
hasHash
|
hasHash,
|
||||||
}: CommentProps) => {
|
}: CommentProps) => {
|
||||||
const [isReplying, setIsReplying] = useState<boolean>(false);
|
const [isReplying, setIsReplying] = useState<boolean>(false);
|
||||||
const [isEditing, setIsEditing] = useState<boolean>(false);
|
const [isEditing, setIsEditing] = useState<boolean>(false);
|
||||||
@ -199,7 +204,7 @@ export const CommentCard = ({
|
|||||||
children,
|
children,
|
||||||
setCurrentEdit,
|
setCurrentEdit,
|
||||||
isReply,
|
isReply,
|
||||||
amount
|
amount,
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const [avatarUrl, setAvatarUrl] = React.useState<string>("");
|
const [avatarUrl, setAvatarUrl] = React.useState<string>("");
|
||||||
const { user } = useSelector((state: RootState) => state.auth);
|
const { user } = useSelector((state: RootState) => state.auth);
|
||||||
@ -225,6 +230,13 @@ export const CommentCard = ({
|
|||||||
getAvatar(name);
|
getAvatar(name);
|
||||||
}, [name]);
|
}, [name]);
|
||||||
|
|
||||||
|
const isScreenSmall = !useMediaQuery(`(min-width:${smallScreenSizeString})`);
|
||||||
|
const superLikeHeaderSX = {
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: isScreenSmall ? "column" : "row",
|
||||||
|
alignItems: "center",
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardContentContainerComment>
|
<CardContentContainerComment>
|
||||||
<StyledCardHeaderComment
|
<StyledCardHeaderComment
|
||||||
@ -234,40 +246,48 @@ export const CommentCard = ({
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box sx={superLikeHeaderSX}>
|
||||||
<Avatar
|
<Box
|
||||||
src={avatarUrl}
|
sx={{
|
||||||
alt={`${name}'s avatar`}
|
display: "flex",
|
||||||
sx={{ width: "35px", height: "35px" }}
|
gap: "10px",
|
||||||
/>
|
alignItems: "center",
|
||||||
</Box>
|
}}
|
||||||
<StyledCardColComment>
|
>
|
||||||
<Box sx={{
|
<Avatar
|
||||||
display: 'flex',
|
src={avatarUrl}
|
||||||
gap: '10px',
|
alt={`${name}'s avatar`}
|
||||||
alignItems: 'center'
|
sx={{ width: "35px", height: "35px" }}
|
||||||
}}>
|
/>
|
||||||
<AuthorTextComment>{name}</AuthorTextComment>
|
<AuthorTextComment>{name}</AuthorTextComment>
|
||||||
{!isReply && (
|
|
||||||
<ThumbUpIcon
|
|
||||||
style={{
|
|
||||||
color: "gold",
|
|
||||||
cursor: "pointer",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{amount && (
|
|
||||||
<Typography sx={{
|
|
||||||
fontSize: '20px',
|
|
||||||
color: 'gold'
|
|
||||||
}}>
|
|
||||||
{parseFloat(amount)?.toFixed(2)} QORT
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
|
<StyledCardColComment
|
||||||
</StyledCardColComment>
|
sx={{
|
||||||
|
marginTop: isScreenSmall ? "10px" : "0px",
|
||||||
|
marginLeft: isScreenSmall ? "0px" : "10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!isReply && (
|
||||||
|
<ThumbUpIcon
|
||||||
|
style={{
|
||||||
|
color: "gold",
|
||||||
|
cursor: "pointer",
|
||||||
|
marginRight: "10px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{amount && (
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: fontSizeSmall,
|
||||||
|
color: "gold",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{parseFloat(amount)?.toFixed(2)} QORT
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</StyledCardColComment>
|
||||||
|
</Box>
|
||||||
</StyledCardHeaderComment>
|
</StyledCardHeaderComment>
|
||||||
<StyledCardContentComment>
|
<StyledCardContentComment>
|
||||||
<StyledCardComment>{message}</StyledCardComment>
|
<StyledCardComment>{message}</StyledCardComment>
|
||||||
|
@ -63,9 +63,9 @@ export const StyledCardCol = styled(Box)({
|
|||||||
export const StyledCardColComment = styled(Box)({
|
export const StyledCardColComment = styled(Box)({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
flexDirection: "column",
|
flexDirection: "row",
|
||||||
gap: "2px",
|
gap: "2px",
|
||||||
alignItems: "flex-start",
|
alignItems: "center",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ export const BlockIconContainer = styled(Box)({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const CommentsContainer = styled(Box)({
|
export const CommentsContainer = styled(Box)({
|
||||||
width: "70%",
|
width: "98%",
|
||||||
maxWidth: "1000px",
|
maxWidth: "1000px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
|
@ -42,7 +42,6 @@ export const MobileControls = () => {
|
|||||||
<IconButton
|
<IconButton
|
||||||
sx={{
|
sx={{
|
||||||
color: "rgba(255, 255, 255, 0.7)",
|
color: "rgba(255, 255, 255, 0.7)",
|
||||||
marginLeft: "15px",
|
|
||||||
}}
|
}}
|
||||||
onClick={reloadVideo}
|
onClick={reloadVideo}
|
||||||
>
|
>
|
||||||
@ -55,11 +54,26 @@ export const MobileControls = () => {
|
|||||||
max={videoRef.current?.duration || 100}
|
max={videoRef.current?.duration || 100}
|
||||||
sx={{ flexGrow: 1, mx: 2 }}
|
sx={{ flexGrow: 1, mx: 2 }}
|
||||||
/>
|
/>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
color: "rgba(255, 255, 255, 0.7)",
|
||||||
|
fontSize: "14px",
|
||||||
|
userSelect: "none",
|
||||||
|
minWidth: "30px",
|
||||||
|
}}
|
||||||
|
onClick={() => increaseSpeed()}
|
||||||
|
>
|
||||||
|
{playbackRate}x
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Fullscreen onClick={toggleFullscreen} />
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
edge="end"
|
edge="end"
|
||||||
color="inherit"
|
color="inherit"
|
||||||
aria-label="menu"
|
aria-label="menu"
|
||||||
onClick={handleMenuOpen}
|
onClick={handleMenuOpen}
|
||||||
|
sx={{ minWidth: "30px" }}
|
||||||
>
|
>
|
||||||
<MoreIcon />
|
<MoreIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@ -85,22 +99,10 @@ export const MobileControls = () => {
|
|||||||
step={0.01}
|
step={0.01}
|
||||||
/>
|
/>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={() => increaseSpeed()}>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
color: "rgba(255, 255, 255, 0.7)",
|
|
||||||
fontSize: "14px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Speed: {playbackRate}x
|
|
||||||
</Typography>
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem onClick={togglePictureInPicture}>
|
<MenuItem onClick={togglePictureInPicture}>
|
||||||
<PictureInPicture />
|
<PictureInPicture />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={toggleFullscreen}>
|
|
||||||
<Fullscreen />
|
|
||||||
</MenuItem>
|
|
||||||
</Menu>
|
</Menu>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -3,11 +3,13 @@ import { useSignalEffect, useSignals } from "@preact/signals-react/runtime";
|
|||||||
|
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { Key } from "ts-key-enum";
|
import { Key } from "ts-key-enum";
|
||||||
import { useIsMobile } from "../../../../hooks/useIsMobile.ts";
|
import { useIsMobile } from "../../../../hooks/useIsMobile.ts";
|
||||||
import { setVideoPlaying } from "../../../../state/features/globalSlice.ts";
|
import { setVideoPlaying } from "../../../../state/features/globalSlice.ts";
|
||||||
import {
|
import {
|
||||||
|
setIsMuted,
|
||||||
|
setMutedVolumeSetting,
|
||||||
setReduxPlaybackRate,
|
setReduxPlaybackRate,
|
||||||
setStretchVideoSetting,
|
setStretchVideoSetting,
|
||||||
setVolumeSetting,
|
setVolumeSetting,
|
||||||
@ -36,13 +38,12 @@ export const useVideoControlsState = (
|
|||||||
progress,
|
progress,
|
||||||
videoObjectFit,
|
videoObjectFit,
|
||||||
canPlay,
|
canPlay,
|
||||||
isMobileView,
|
|
||||||
} = videoPlayerState;
|
} = videoPlayerState;
|
||||||
const { identifier, autoPlay } = props;
|
const { identifier, autoPlay } = props;
|
||||||
|
|
||||||
const isMobile = useIsMobile();
|
|
||||||
const showControlsFullScreen = useSignal(true);
|
const showControlsFullScreen = useSignal(true);
|
||||||
const persistSelector = store.getState().persist;
|
const dispatch = useDispatch();
|
||||||
|
const persistSelector = useSelector((root: RootState) => root.persist);
|
||||||
|
|
||||||
const videoPlaying = useSelector(
|
const videoPlaying = useSelector(
|
||||||
(state: RootState) => state.global.videoPlaying
|
(state: RootState) => state.global.videoPlaying
|
||||||
@ -58,7 +59,6 @@ export const useVideoControlsState = (
|
|||||||
|
|
||||||
videoRef.current.playbackRate = newSpeed;
|
videoRef.current.playbackRate = newSpeed;
|
||||||
playbackRate.value = newSpeed;
|
playbackRate.value = newSpeed;
|
||||||
store.dispatch(setReduxPlaybackRate(newSpeed));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -156,10 +156,9 @@ export const useVideoControlsState = (
|
|||||||
const onVolumeChange = (_: any, value: number | number[]) => {
|
const onVolumeChange = (_: any, value: number | number[]) => {
|
||||||
if (!videoRef.current) return;
|
if (!videoRef.current) return;
|
||||||
const newVolume = value as number;
|
const newVolume = value as number;
|
||||||
videoRef.current.volume = newVolume;
|
|
||||||
volume.value = newVolume;
|
|
||||||
isMuted.value = false;
|
isMuted.value = false;
|
||||||
store.dispatch(setVolumeSetting(newVolume));
|
mutedVolume.value = newVolume;
|
||||||
|
volume.value = newVolume;
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -170,12 +169,10 @@ export const useVideoControlsState = (
|
|||||||
isMuted.value = true;
|
isMuted.value = true;
|
||||||
mutedVolume.value = volume.value;
|
mutedVolume.value = volume.value;
|
||||||
volume.value = 0;
|
volume.value = 0;
|
||||||
if (videoRef.current) videoRef.current.volume = 0;
|
|
||||||
};
|
};
|
||||||
const unMute = () => {
|
const unMute = () => {
|
||||||
isMuted.value = false;
|
isMuted.value = false;
|
||||||
volume.value = mutedVolume.value;
|
volume.value = mutedVolume.value;
|
||||||
if (videoRef.current) videoRef.current.volume = mutedVolume.value;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleMute = () => {
|
const toggleMute = () => {
|
||||||
@ -191,12 +188,10 @@ export const useVideoControlsState = (
|
|||||||
|
|
||||||
newVolume = Math.max(newVolume, minVolume);
|
newVolume = Math.max(newVolume, minVolume);
|
||||||
newVolume = Math.min(newVolume, maxVolume);
|
newVolume = Math.min(newVolume, maxVolume);
|
||||||
|
newVolume = +newVolume.toFixed(2);
|
||||||
isMuted.value = false;
|
isMuted.value = false;
|
||||||
mutedVolume.value = newVolume;
|
mutedVolume.value = newVolume;
|
||||||
videoRef.current.volume = newVolume;
|
|
||||||
volume.value = newVolume;
|
volume.value = newVolume;
|
||||||
store.dispatch(setVolumeSetting(newVolume));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const setProgressRelative = (secondsChange: number) => {
|
const setProgressRelative = (secondsChange: number) => {
|
||||||
@ -228,7 +223,6 @@ export const useVideoControlsState = (
|
|||||||
persistSelector.stretchVideoSetting === "contain" ? "fill" : "contain";
|
persistSelector.stretchVideoSetting === "contain" ? "fill" : "contain";
|
||||||
|
|
||||||
videoObjectFit.value = newStretchVideoSetting;
|
videoObjectFit.value = newStretchVideoSetting;
|
||||||
store.dispatch(setStretchVideoSetting(newStretchVideoSetting));
|
|
||||||
};
|
};
|
||||||
const keyboardShortcutsDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
const keyboardShortcutsDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -350,14 +344,13 @@ export const useVideoControlsState = (
|
|||||||
videoRef.current.play().then(() => {
|
videoRef.current.play().then(() => {
|
||||||
playing.value = true;
|
playing.value = true;
|
||||||
startPlay.value = true;
|
startPlay.value = true;
|
||||||
store.dispatch(setVideoPlaying(null));
|
dispatch(setVideoPlaying(null));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [videoPlaying, identifier, src]);
|
}, [videoPlaying, identifier, src]);
|
||||||
|
|
||||||
useSignalEffect(() => {
|
useSignalEffect(() => {
|
||||||
console.log("canPlay is: ", canPlay.value); // makes the function execute when canPlay changes
|
console.log("canPlay is: ", canPlay.value); // makes the function execute when canPlay changes
|
||||||
if (isMobile) isMobileView.value = true;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -7,12 +7,14 @@ import {
|
|||||||
VolumeOff,
|
VolumeOff,
|
||||||
VolumeUp,
|
VolumeUp,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import { IconButton, Slider, Typography } from "@mui/material";
|
import { IconButton, Slider, Typography, useMediaQuery } from "@mui/material";
|
||||||
|
import { smallScreenSizeString } from "../../../../constants/Misc.ts";
|
||||||
import { formatTime } from "../../../../utils/numberFunctions.ts";
|
import { formatTime } from "../../../../utils/numberFunctions.ts";
|
||||||
|
|
||||||
import { ControlsContainer } from "../VideoPlayer-styles.ts";
|
import { ControlsContainer } from "../VideoPlayer-styles.ts";
|
||||||
import { MobileControls } from "./MobileControls.tsx";
|
import { MobileControls } from "./MobileControls.tsx";
|
||||||
import { useVideoContext } from "./VideoContext.ts";
|
import { useVideoContext } from "./VideoContext.ts";
|
||||||
|
import { useSignalEffect } from "@preact/signals-react";
|
||||||
|
|
||||||
export const VideoControls = () => {
|
export const VideoControls = () => {
|
||||||
const {
|
const {
|
||||||
@ -28,7 +30,6 @@ export const VideoControls = () => {
|
|||||||
from,
|
from,
|
||||||
videoRef,
|
videoRef,
|
||||||
canPlay,
|
canPlay,
|
||||||
isMobileView,
|
|
||||||
isMuted,
|
isMuted,
|
||||||
playbackRate,
|
playbackRate,
|
||||||
playing,
|
playing,
|
||||||
@ -37,8 +38,8 @@ export const VideoControls = () => {
|
|||||||
showControlsFullScreen,
|
showControlsFullScreen,
|
||||||
} = useVideoContext();
|
} = useVideoContext();
|
||||||
|
|
||||||
const showMobileControls =
|
const isScreenSmall = !useMediaQuery(`(min-width:580px)`);
|
||||||
isMobileView.value && canPlay.value && showControlsFullScreen.value;
|
const showMobileControls = isScreenSmall && canPlay.value;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ControlsContainer
|
<ControlsContainer
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import { useSignal, useSignals } from "@preact/signals-react/runtime";
|
import {
|
||||||
|
useSignal,
|
||||||
|
useSignalEffect,
|
||||||
|
useSignals,
|
||||||
|
} from "@preact/signals-react/runtime";
|
||||||
import React, {
|
import React, {
|
||||||
useContext,
|
useContext,
|
||||||
useEffect,
|
useEffect,
|
||||||
@ -9,7 +13,14 @@ import React, {
|
|||||||
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { setVideoPlaying } from "../../../state/features/globalSlice.ts";
|
import { setVideoPlaying } from "../../../state/features/globalSlice.ts";
|
||||||
import { StretchVideoType } from "../../../state/features/persistSlice.ts";
|
import {
|
||||||
|
setIsMuted,
|
||||||
|
setMutedVolumeSetting,
|
||||||
|
setReduxPlaybackRate,
|
||||||
|
setStretchVideoSetting,
|
||||||
|
setVolumeSetting,
|
||||||
|
StretchVideoType,
|
||||||
|
} from "../../../state/features/persistSlice.ts";
|
||||||
import { RootState } from "../../../state/store.ts";
|
import { RootState } from "../../../state/store.ts";
|
||||||
import { MyContext } from "../../../wrappers/DownloadWrapper.tsx";
|
import { MyContext } from "../../../wrappers/DownloadWrapper.tsx";
|
||||||
import { VideoPlayerProps } from "./VideoPlayer.tsx";
|
import { VideoPlayerProps } from "./VideoPlayer.tsx";
|
||||||
@ -19,20 +30,40 @@ export const useVideoPlayerState = (props: VideoPlayerProps, ref: any) => {
|
|||||||
const persistSelector = useSelector((state: RootState) => state.persist);
|
const persistSelector = useSelector((state: RootState) => state.persist);
|
||||||
|
|
||||||
const playing = useSignal(false);
|
const playing = useSignal(false);
|
||||||
const isMuted = useSignal(false);
|
|
||||||
const progress = useSignal(0);
|
const progress = useSignal(0);
|
||||||
const isLoading = useSignal(false);
|
const isLoading = useSignal(false);
|
||||||
const canPlay = useSignal(false);
|
const canPlay = useSignal(false);
|
||||||
const startPlay = useSignal(false);
|
const startPlay = useSignal(false);
|
||||||
const isMobileView = useSignal(false);
|
|
||||||
|
const isMuted = useSignal(persistSelector.isMuted);
|
||||||
const volume = useSignal(persistSelector.volume);
|
const volume = useSignal(persistSelector.volume);
|
||||||
const mutedVolume = useSignal(persistSelector.volume);
|
const mutedVolume = useSignal(persistSelector.mutedVolume);
|
||||||
const playbackRate = useSignal(persistSelector.playbackRate);
|
const playbackRate = useSignal(persistSelector.playbackRate);
|
||||||
const anchorEl = useSignal(null);
|
|
||||||
const videoObjectFit = useSignal<StretchVideoType>(
|
const videoObjectFit = useSignal<StretchVideoType>(
|
||||||
persistSelector.stretchVideoSetting
|
persistSelector.stretchVideoSetting
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useSignalEffect(() => {
|
||||||
|
dispatch(setIsMuted(isMuted.value));
|
||||||
|
});
|
||||||
|
|
||||||
|
useSignalEffect(() => {
|
||||||
|
dispatch(setVolumeSetting(volume.value));
|
||||||
|
if (videoRef.current) videoRef.current.volume = volume.value;
|
||||||
|
});
|
||||||
|
useSignalEffect(() => {
|
||||||
|
dispatch(setMutedVolumeSetting(mutedVolume.value));
|
||||||
|
});
|
||||||
|
useSignalEffect(() => {
|
||||||
|
if (videoRef.current) videoRef.current.playbackRate = playbackRate.value;
|
||||||
|
dispatch(setReduxPlaybackRate(playbackRate.value));
|
||||||
|
});
|
||||||
|
useSignalEffect(() => {
|
||||||
|
dispatch(setStretchVideoSetting(videoObjectFit.value));
|
||||||
|
});
|
||||||
|
|
||||||
|
const anchorEl = useSignal(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
identifier,
|
identifier,
|
||||||
@ -279,7 +310,6 @@ export const useVideoPlayerState = (props: VideoPlayerProps, ref: any) => {
|
|||||||
isLoading,
|
isLoading,
|
||||||
canPlay,
|
canPlay,
|
||||||
startPlay,
|
startPlay,
|
||||||
isMobileView,
|
|
||||||
volume,
|
volume,
|
||||||
mutedVolume,
|
mutedVolume,
|
||||||
playbackRate,
|
playbackRate,
|
||||||
|
@ -55,7 +55,6 @@ export const VideoPlayer = forwardRef<videoRefType, VideoPlayerProps>(
|
|||||||
startPlay,
|
startPlay,
|
||||||
videoObjectFit,
|
videoObjectFit,
|
||||||
showControlsFullScreen,
|
showControlsFullScreen,
|
||||||
duration,
|
|
||||||
} = contextData;
|
} = contextData;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
73
src/components/layout/Navbar/Components/PublishMenu.tsx
Normal file
73
src/components/layout/Navbar/Components/PublishMenu.tsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { headerIconSize, menuIconSize } from "../../../../constants/Misc.ts";
|
||||||
|
import { setEditPlaylist } from "../../../../state/features/videoSlice.ts";
|
||||||
|
import { StyledButton } from "../../../Publish/PublishVideo/PublishVideo-styles.tsx";
|
||||||
|
import { PublishVideo } from "../../../Publish/PublishVideo/PublishVideo.tsx";
|
||||||
|
import {
|
||||||
|
AvatarContainer,
|
||||||
|
DropdownContainer,
|
||||||
|
DropdownText,
|
||||||
|
NavbarName,
|
||||||
|
} from "../Navbar-styles.tsx";
|
||||||
|
import AddBoxIcon from "@mui/icons-material/AddBox";
|
||||||
|
import { PopMenu, PopMenuRefType } from "../../../common/PopMenu.tsx";
|
||||||
|
import { useRef } from "react";
|
||||||
|
import { useMediaQuery } from "@mui/material";
|
||||||
|
|
||||||
|
export interface PublishButtonsProps {
|
||||||
|
isDisplayed: boolean;
|
||||||
|
}
|
||||||
|
import PlaylistAddIcon from "@mui/icons-material/PlaylistAdd";
|
||||||
|
|
||||||
|
export const PublishMenu = ({ isDisplayed }: PublishButtonsProps) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const popMenuRef = useRef<PopMenuRefType>(null);
|
||||||
|
|
||||||
|
const isScreenSmall = !useMediaQuery(`(min-width:600px)`);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isDisplayed && (
|
||||||
|
<PopMenu
|
||||||
|
MenuHeader={
|
||||||
|
<>
|
||||||
|
{!isScreenSmall && (
|
||||||
|
<NavbarName sx={{ marginRight: "5px" }}>Publish</NavbarName>
|
||||||
|
)}
|
||||||
|
<AddBoxIcon
|
||||||
|
sx={{
|
||||||
|
color: "DarkGreen",
|
||||||
|
width: headerIconSize,
|
||||||
|
height: headerIconSize,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
ref={popMenuRef}
|
||||||
|
>
|
||||||
|
<DropdownContainer>
|
||||||
|
<PublishVideo afterClose={popMenuRef?.current?.closePopover} />
|
||||||
|
</DropdownContainer>
|
||||||
|
<DropdownContainer onClick={popMenuRef?.current?.closePopover}>
|
||||||
|
<StyledButton
|
||||||
|
color="primary"
|
||||||
|
startIcon={
|
||||||
|
<PlaylistAddIcon
|
||||||
|
sx={{
|
||||||
|
color: "#00BFFF",
|
||||||
|
width: menuIconSize,
|
||||||
|
height: menuIconSize,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(setEditPlaylist({ mode: "new" }));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Playlist
|
||||||
|
</StyledButton>
|
||||||
|
</DropdownContainer>
|
||||||
|
</PopMenu>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
40
src/components/layout/Navbar/Components/QtubeLogo.tsx
Normal file
40
src/components/layout/Navbar/Components/QtubeLogo.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import Logo from "../../../../assets/img/logo.webp";
|
||||||
|
import {
|
||||||
|
addFilteredVideos,
|
||||||
|
setFilterValue,
|
||||||
|
setIsFiltering,
|
||||||
|
} from "../../../../state/features/videoSlice.ts";
|
||||||
|
import { LogoContainer, ThemeSelectRow } from "../Navbar-styles.tsx";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { useMediaQuery } from "@mui/material";
|
||||||
|
|
||||||
|
export const QtubeLogo = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const isScreenSmall = !useMediaQuery(`(min-width:600px)`);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeSelectRow>
|
||||||
|
<LogoContainer
|
||||||
|
onClick={() => {
|
||||||
|
navigate("/");
|
||||||
|
dispatch(setIsFiltering(false));
|
||||||
|
dispatch(setFilterValue(""));
|
||||||
|
dispatch(addFilteredVideos([]));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={Logo}
|
||||||
|
style={{
|
||||||
|
width: isScreenSmall ? "50px" : "auto",
|
||||||
|
height: "45px",
|
||||||
|
padding: "2px",
|
||||||
|
marginTop: "5px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</LogoContainer>
|
||||||
|
</ThemeSelectRow>
|
||||||
|
);
|
||||||
|
};
|
124
src/components/layout/Navbar/Components/UserMenu.tsx
Normal file
124
src/components/layout/Navbar/Components/UserMenu.tsx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import { Popover, useMediaQuery, useTheme } from "@mui/material";
|
||||||
|
import { AccountCircleSVG } from "../../../../assets/svgs/AccountCircleSVG.tsx";
|
||||||
|
import { headerIconSize, menuIconSize } from "../../../../constants/Misc.ts";
|
||||||
|
import { BlockedNamesModal } from "../../../common/BlockedNamesModal/BlockedNamesModal.tsx";
|
||||||
|
import {
|
||||||
|
AvatarContainer,
|
||||||
|
DropdownContainer,
|
||||||
|
DropdownText,
|
||||||
|
NavbarName,
|
||||||
|
} from "../Navbar-styles.tsx";
|
||||||
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||||
|
import { useRef, useState } from "react";
|
||||||
|
|
||||||
|
import PersonOffIcon from "@mui/icons-material/PersonOff";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { PopMenu, PopMenuRefType } from "../../../common/PopMenu.tsx";
|
||||||
|
|
||||||
|
export interface NavBarMenuProps {
|
||||||
|
isShowMenu: boolean;
|
||||||
|
userAvatar: string;
|
||||||
|
userName: string | null;
|
||||||
|
}
|
||||||
|
export const UserMenu = ({
|
||||||
|
isShowMenu,
|
||||||
|
userAvatar,
|
||||||
|
userName,
|
||||||
|
}: NavBarMenuProps) => {
|
||||||
|
const isScreenSmall = !useMediaQuery(`(min-width:600px)`);
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const [isOpenBlockedNamesModal, setIsOpenBlockedNamesModal] =
|
||||||
|
useState<boolean>(false);
|
||||||
|
const popMenuRef = useRef<PopMenuRefType>(null);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleMyChannelLink = () => {
|
||||||
|
navigate(`/channel/${userName}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCloseBlockedNames = () => {
|
||||||
|
setIsOpenBlockedNamesModal(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isShowMenu && (
|
||||||
|
<>
|
||||||
|
<PopMenu
|
||||||
|
ref={popMenuRef}
|
||||||
|
MenuHeader={
|
||||||
|
<AvatarContainer>
|
||||||
|
{!isScreenSmall && <NavbarName>{userName}</NavbarName>}
|
||||||
|
{!userAvatar ? (
|
||||||
|
<AccountCircleSVG
|
||||||
|
color={theme.palette.text.primary}
|
||||||
|
width={headerIconSize}
|
||||||
|
height={headerIconSize}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<img
|
||||||
|
src={userAvatar}
|
||||||
|
alt="User Avatar"
|
||||||
|
width={headerIconSize}
|
||||||
|
height={headerIconSize}
|
||||||
|
style={{
|
||||||
|
borderRadius: "50%",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AvatarContainer>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<DropdownContainer
|
||||||
|
onClick={() => {
|
||||||
|
handleMyChannelLink();
|
||||||
|
popMenuRef.current.closePopover();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!userAvatar ? (
|
||||||
|
<AccountCircleSVG
|
||||||
|
color={theme.palette.text.primary}
|
||||||
|
width={menuIconSize}
|
||||||
|
height={menuIconSize}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<img
|
||||||
|
src={userAvatar}
|
||||||
|
alt="User Avatar"
|
||||||
|
width={menuIconSize}
|
||||||
|
height={menuIconSize}
|
||||||
|
style={{
|
||||||
|
borderRadius: "50%",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<DropdownText>{userName}</DropdownText>
|
||||||
|
</DropdownContainer>
|
||||||
|
<DropdownContainer
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpenBlockedNamesModal(true);
|
||||||
|
popMenuRef.current.closePopover();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PersonOffIcon
|
||||||
|
sx={{
|
||||||
|
color: "#e35050",
|
||||||
|
width: menuIconSize,
|
||||||
|
height: menuIconSize,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<DropdownText>Blocked Names</DropdownText>
|
||||||
|
</DropdownContainer>
|
||||||
|
</PopMenu>
|
||||||
|
{isOpenBlockedNamesModal && (
|
||||||
|
<BlockedNamesModal
|
||||||
|
open={isOpenBlockedNamesModal}
|
||||||
|
onClose={onCloseBlockedNames}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -2,20 +2,18 @@ import { AppBar, Button, Typography, Box } from "@mui/material";
|
|||||||
import { styled } from "@mui/system";
|
import { styled } from "@mui/system";
|
||||||
import { LightModeSVG } from "../../../assets/svgs/LightModeSVG";
|
import { LightModeSVG } from "../../../assets/svgs/LightModeSVG";
|
||||||
import { DarkModeSVG } from "../../../assets/svgs/DarkModeSVG";
|
import { DarkModeSVG } from "../../../assets/svgs/DarkModeSVG";
|
||||||
|
import { fontSizeSmall } from "../../../constants/Misc.ts";
|
||||||
|
|
||||||
export const CustomAppBar = styled(AppBar)(({ theme }) => ({
|
export const CustomAppBar = styled(AppBar)(({ theme }) => ({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: "space-between",
|
justifyContent: "start",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
padding: "5px 16px",
|
padding: "5px 16px 5px 5px",
|
||||||
backgroundImage: "none",
|
backgroundImage: "none",
|
||||||
borderBottom: `1px solid ${theme.palette.primary.light}`,
|
borderBottom: `1px solid ${theme.palette.primary.light}`,
|
||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.background.default,
|
||||||
[theme.breakpoints.only("xs")]: {
|
|
||||||
gap: "15px",
|
|
||||||
},
|
|
||||||
height: "50px",
|
height: "50px",
|
||||||
}));
|
}));
|
||||||
export const LogoContainer = styled("div")({
|
export const LogoContainer = styled("div")({
|
||||||
@ -85,16 +83,15 @@ export const DropdownText = styled(Typography)(({ theme }) => ({
|
|||||||
|
|
||||||
export const NavbarName = styled(Typography)(({ theme }) => ({
|
export const NavbarName = styled(Typography)(({ theme }) => ({
|
||||||
fontFamily: "Raleway",
|
fontFamily: "Raleway",
|
||||||
fontSize: "18px",
|
fontSize: fontSizeSmall,
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
margin: "0 10px",
|
marginRight: "10px",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const ThemeSelectRow = styled(Box)({
|
export const ThemeSelectRow = styled(Box)({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: "5px",
|
gap: "10px",
|
||||||
flexBasis: 0,
|
|
||||||
height: "100%",
|
height: "100%",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,43 +1,12 @@
|
|||||||
import React, { useState, useRef } from "react";
|
import { Box, useMediaQuery } from "@mui/material";
|
||||||
import { Box, Button, Input, Popover, useTheme } from "@mui/material";
|
import React from "react";
|
||||||
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
|
|
||||||
import { BlockedNamesModal } from "../../common/BlockedNamesModal/BlockedNamesModal";
|
|
||||||
import AddBoxIcon from "@mui/icons-material/AddBox";
|
|
||||||
|
|
||||||
import {
|
|
||||||
AvatarContainer,
|
|
||||||
CustomAppBar,
|
|
||||||
DropdownContainer,
|
|
||||||
DropdownText,
|
|
||||||
AuthenticateButton,
|
|
||||||
NavbarName,
|
|
||||||
LightModeIcon,
|
|
||||||
DarkModeIcon,
|
|
||||||
ThemeSelectRow,
|
|
||||||
LogoContainer,
|
|
||||||
} from "./Navbar-styles";
|
|
||||||
import { AccountCircleSVG } from "../../../assets/svgs/AccountCircleSVG";
|
|
||||||
import BackspaceIcon from "@mui/icons-material/Backspace";
|
|
||||||
|
|
||||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
|
||||||
import PersonOffIcon from "@mui/icons-material/PersonOff";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
import SearchIcon from "@mui/icons-material/Search";
|
|
||||||
|
|
||||||
import { DownloadTaskManager } from "../../common/DownloadTaskManager";
|
import { DownloadTaskManager } from "../../common/DownloadTaskManager";
|
||||||
import Logo from "../../../assets/img/logo.webp";
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
|
||||||
import {
|
|
||||||
addFilteredVideos,
|
|
||||||
setEditPlaylist,
|
|
||||||
setFilterValue,
|
|
||||||
setIsFiltering,
|
|
||||||
} from "../../../state/features/videoSlice";
|
|
||||||
import { RootState } from "../../../state/store";
|
|
||||||
import { useWindowSize } from "../../../hooks/useWindowSize";
|
|
||||||
import { PublishVideo } from "../../Publish/PublishVideo/PublishVideo.tsx";
|
|
||||||
import { StyledButton } from "../../Publish/PublishVideo/PublishVideo-styles.tsx";
|
|
||||||
import { Notifications } from "../../common/Notifications/Notifications";
|
import { Notifications } from "../../common/Notifications/Notifications";
|
||||||
|
import { PublishMenu } from "./Components/PublishMenu.tsx";
|
||||||
|
import { QtubeLogo } from "./Components/QtubeLogo.tsx";
|
||||||
|
import { UserMenu } from "./Components/UserMenu.tsx";
|
||||||
|
import { CustomAppBar } from "./Navbar-styles";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isAuthenticated: boolean;
|
isAuthenticated: boolean;
|
||||||
userName: string | null;
|
userName: string | null;
|
||||||
@ -46,296 +15,39 @@ interface Props {
|
|||||||
setTheme: (val: string) => void;
|
setTheme: (val: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NavBar: React.FC<Props> = ({
|
const NavBar: React.FC<Props> = ({ isAuthenticated, userName, userAvatar }) => {
|
||||||
isAuthenticated,
|
const isScreenSmall = !useMediaQuery(`(min-width:600px)`);
|
||||||
userName,
|
const isSecure = isAuthenticated && !!userName;
|
||||||
userAvatar,
|
const gapSize = 10;
|
||||||
authenticate,
|
|
||||||
setTheme,
|
|
||||||
}) => {
|
|
||||||
const windowSize = useWindowSize();
|
|
||||||
const searchValRef = useRef("");
|
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
|
||||||
const theme = useTheme();
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(
|
|
||||||
null
|
|
||||||
);
|
|
||||||
const [openUserDropdown, setOpenUserDropdown] = useState<boolean>(false);
|
|
||||||
const [isOpenBlockedNamesModal, setIsOpenBlockedNamesModal] =
|
|
||||||
useState<boolean>(false);
|
|
||||||
|
|
||||||
const [anchorElNotification, setAnchorElNotification] =
|
|
||||||
React.useState<HTMLButtonElement | null>(null);
|
|
||||||
const filterValue = useSelector(
|
|
||||||
(state: RootState) => state.video.filterValue
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
|
||||||
const target = event.currentTarget as unknown as HTMLButtonElement | null;
|
|
||||||
setAnchorEl(target);
|
|
||||||
};
|
|
||||||
const openNotificationPopover = (event: any) => {
|
|
||||||
const target = event.currentTarget as unknown as HTMLButtonElement | null;
|
|
||||||
setAnchorElNotification(target);
|
|
||||||
};
|
|
||||||
const closeNotificationPopover = () => {
|
|
||||||
setAnchorElNotification(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const openPopover = Boolean(anchorElNotification);
|
|
||||||
const idNotification = openPopover
|
|
||||||
? "simple-popover-notification"
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const handleCloseUserDropdown = () => {
|
|
||||||
setAnchorEl(null);
|
|
||||||
setOpenUserDropdown(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMyChannelLink = () => {
|
|
||||||
navigate(`/channel/${userName}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCloseBlockedNames = () => {
|
|
||||||
setIsOpenBlockedNamesModal(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomAppBar position="sticky" elevation={2}>
|
<CustomAppBar position="sticky" elevation={2}>
|
||||||
<ThemeSelectRow>
|
|
||||||
<LogoContainer
|
|
||||||
onClick={() => {
|
|
||||||
navigate("/");
|
|
||||||
dispatch(setIsFiltering(false));
|
|
||||||
dispatch(setFilterValue(""));
|
|
||||||
dispatch(addFilteredVideos([]));
|
|
||||||
searchValRef.current = "";
|
|
||||||
if (!inputRef.current) return;
|
|
||||||
inputRef.current.value = "";
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={Logo}
|
|
||||||
style={{
|
|
||||||
width: "auto",
|
|
||||||
height: "45px",
|
|
||||||
padding: "2px",
|
|
||||||
marginTop: "5px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</LogoContainer>
|
|
||||||
</ThemeSelectRow>
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
width: "100%",
|
||||||
gap: "10px",
|
justifyContent: "space-between",
|
||||||
|
gap: `${gapSize}px`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Popover
|
<QtubeLogo />
|
||||||
id={idNotification}
|
<Box
|
||||||
open={openPopover}
|
sx={{
|
||||||
anchorEl={anchorElNotification}
|
display: "flex",
|
||||||
onClose={closeNotificationPopover}
|
gap: `${isScreenSmall ? gapSize : gapSize * 2}px`,
|
||||||
anchorOrigin={{
|
alignItems: "center",
|
||||||
vertical: "bottom",
|
|
||||||
horizontal: "left",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
{isSecure && <Notifications />}
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 1,
|
|
||||||
padding: "5px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
id="standard-adornment-name"
|
|
||||||
inputRef={inputRef}
|
|
||||||
onChange={e => {
|
|
||||||
searchValRef.current = e.target.value;
|
|
||||||
}}
|
|
||||||
onKeyDown={event => {
|
|
||||||
if (event.key === "Enter" || event.keyCode === 13) {
|
|
||||||
if (!searchValRef.current) {
|
|
||||||
dispatch(setIsFiltering(false));
|
|
||||||
dispatch(setFilterValue(""));
|
|
||||||
dispatch(addFilteredVideos([]));
|
|
||||||
searchValRef.current = "";
|
|
||||||
if (!inputRef.current) return;
|
|
||||||
inputRef.current.value = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
navigate("/");
|
|
||||||
dispatch(setIsFiltering(true));
|
|
||||||
dispatch(addFilteredVideos([]));
|
|
||||||
dispatch(setFilterValue(searchValRef.current));
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
placeholder="Search"
|
|
||||||
sx={{
|
|
||||||
"&&:before": {
|
|
||||||
borderBottom: "none",
|
|
||||||
},
|
|
||||||
"&&:after": {
|
|
||||||
borderBottom: "none",
|
|
||||||
},
|
|
||||||
"&&:hover:before": {
|
|
||||||
borderBottom: "none",
|
|
||||||
},
|
|
||||||
"&&.Mui-focused:before": {
|
|
||||||
borderBottom: "none",
|
|
||||||
},
|
|
||||||
"&&.Mui-focused": {
|
|
||||||
outline: "none",
|
|
||||||
},
|
|
||||||
fontSize: "18px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SearchIcon
|
<DownloadTaskManager />
|
||||||
sx={{
|
<UserMenu
|
||||||
cursor: "pointer",
|
isShowMenu={isSecure}
|
||||||
}}
|
userAvatar={userAvatar}
|
||||||
onClick={() => {
|
userName={userName}
|
||||||
if (!searchValRef.current) {
|
|
||||||
dispatch(setIsFiltering(false));
|
|
||||||
dispatch(setFilterValue(""));
|
|
||||||
dispatch(addFilteredVideos([]));
|
|
||||||
searchValRef.current = "";
|
|
||||||
if (!inputRef.current) return;
|
|
||||||
inputRef.current.value = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
navigate("/");
|
|
||||||
dispatch(setIsFiltering(true));
|
|
||||||
dispatch(addFilteredVideos([]));
|
|
||||||
dispatch(setFilterValue(searchValRef.current));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<BackspaceIcon
|
|
||||||
sx={{
|
|
||||||
cursor: "pointer",
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
dispatch(setIsFiltering(false));
|
|
||||||
dispatch(setFilterValue(""));
|
|
||||||
dispatch(addFilteredVideos([]));
|
|
||||||
searchValRef.current = "";
|
|
||||||
if (!inputRef.current) return;
|
|
||||||
inputRef.current.value = "";
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Popover>
|
|
||||||
{isAuthenticated && userName && <Notifications />}
|
|
||||||
|
|
||||||
<DownloadTaskManager />
|
|
||||||
{isAuthenticated && userName && (
|
|
||||||
<>
|
|
||||||
<AvatarContainer
|
|
||||||
onClick={(e: any) => {
|
|
||||||
handleClick(e);
|
|
||||||
setOpenUserDropdown(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<NavbarName>{userName}</NavbarName>
|
|
||||||
{!userAvatar ? (
|
|
||||||
<AccountCircleSVG
|
|
||||||
color={theme.palette.text.primary}
|
|
||||||
width="40"
|
|
||||||
height="40"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<img
|
|
||||||
src={userAvatar}
|
|
||||||
alt="User Avatar"
|
|
||||||
width="40"
|
|
||||||
height="40"
|
|
||||||
style={{
|
|
||||||
borderRadius: "50%",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<ExpandMoreIcon id="expand-icon" sx={{ color: "#ACB6BF" }} />
|
|
||||||
</AvatarContainer>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<AvatarContainer>
|
|
||||||
{isAuthenticated && userName && (
|
|
||||||
<>
|
|
||||||
<PublishVideo />
|
|
||||||
<StyledButton
|
|
||||||
color="primary"
|
|
||||||
startIcon={<AddBoxIcon />}
|
|
||||||
onClick={() => {
|
|
||||||
dispatch(setEditPlaylist({ mode: "new" }));
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
create playlist
|
|
||||||
</StyledButton>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</AvatarContainer>
|
|
||||||
|
|
||||||
<Popover
|
|
||||||
id={"user-popover"}
|
|
||||||
open={openUserDropdown}
|
|
||||||
anchorEl={anchorEl}
|
|
||||||
onClose={handleCloseUserDropdown}
|
|
||||||
anchorOrigin={{
|
|
||||||
vertical: "bottom",
|
|
||||||
horizontal: "left",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DropdownContainer
|
|
||||||
onClick={() => {
|
|
||||||
handleMyChannelLink();
|
|
||||||
handleCloseUserDropdown();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{!userAvatar ? (
|
|
||||||
<AccountCircleSVG
|
|
||||||
color={theme.palette.text.primary}
|
|
||||||
width="28"
|
|
||||||
height="28"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<img
|
|
||||||
src={userAvatar}
|
|
||||||
alt="User Avatar"
|
|
||||||
width="28"
|
|
||||||
height="28"
|
|
||||||
style={{
|
|
||||||
borderRadius: "50%",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<DropdownText>My Channel</DropdownText>
|
|
||||||
</DropdownContainer>
|
|
||||||
<DropdownContainer
|
|
||||||
onClick={() => {
|
|
||||||
setIsOpenBlockedNamesModal(true);
|
|
||||||
handleCloseUserDropdown();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PersonOffIcon
|
|
||||||
sx={{
|
|
||||||
color: "#e35050",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<DropdownText>Blocked Names</DropdownText>
|
|
||||||
</DropdownContainer>
|
|
||||||
</Popover>
|
|
||||||
{isOpenBlockedNamesModal && (
|
|
||||||
<BlockedNamesModal
|
|
||||||
open={isOpenBlockedNamesModal}
|
|
||||||
onClose={onCloseBlockedNames}
|
|
||||||
/>
|
/>
|
||||||
)}
|
<PublishMenu isDisplayed={isSecure} />
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</CustomAppBar>
|
</CustomAppBar>
|
||||||
);
|
);
|
||||||
|
@ -14,3 +14,12 @@ export const fontSizeExLarge = "150%";
|
|||||||
export const maxCommentLength = 10_000;
|
export const maxCommentLength = 10_000;
|
||||||
export const minFileSize = 1_000;
|
export const minFileSize = 1_000;
|
||||||
export const minDuration = 5;
|
export const minDuration = 5;
|
||||||
|
|
||||||
|
const newUIWidthDiff = 120;
|
||||||
|
const smallScreenSize = 700 - newUIWidthDiff;
|
||||||
|
const largeScreenSize = 1400 - newUIWidthDiff;
|
||||||
|
export const smallScreenSizeString = `${smallScreenSize}px`;
|
||||||
|
export const largeScreenSizeString = `${largeScreenSize}px`;
|
||||||
|
|
||||||
|
export const headerIconSize = "40px";
|
||||||
|
export const menuIconSize = "28px";
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
import { Box, Typography } from "@mui/material";
|
import { Box, Grid, Typography, useMediaQuery } from "@mui/material";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { CommentSection } from "../../../components/common/Comments/CommentSection.tsx";
|
import { CommentSection } from "../../../components/common/Comments/CommentSection.tsx";
|
||||||
import { SuperLikesSection } from "../../../components/common/SuperLikesList/SuperLikesSection.tsx";
|
import { SuperLikesSection } from "../../../components/common/SuperLikesList/SuperLikesSection.tsx";
|
||||||
import { DisplayHtml } from "../../../components/common/TextEditor/DisplayHtml.tsx";
|
import { DisplayHtml } from "../../../components/common/TextEditor/DisplayHtml.tsx";
|
||||||
import { VideoPlayer } from "../../../components/common/VideoPlayer/VideoPlayer.tsx";
|
import { VideoPlayer } from "../../../components/common/VideoPlayer/VideoPlayer.tsx";
|
||||||
import { Playlists } from "../../../components/Playlists/Playlists.tsx";
|
import { Playlists } from "../../../components/Playlists/Playlists.tsx";
|
||||||
|
import {
|
||||||
|
fontSizeSmall,
|
||||||
|
minFileSize,
|
||||||
|
smallScreenSizeString,
|
||||||
|
} from "../../../constants/Misc.ts";
|
||||||
|
import { formatBytes } from "../../../utils/numberFunctions.ts";
|
||||||
import { formatDate } from "../../../utils/time.ts";
|
import { formatDate } from "../../../utils/time.ts";
|
||||||
import { VideoActionsBar } from "../VideoContent/VideoActionsBar.tsx";
|
import { VideoActionsBar } from "../VideoContent/VideoActionsBar.tsx";
|
||||||
import { usePlaylistContentState } from "./PlaylistContent-State.ts";
|
import { usePlaylistContentState } from "./PlaylistContent-State.ts";
|
||||||
@ -40,6 +46,8 @@ export const PlaylistContent = () => {
|
|||||||
loadingSuperLikes,
|
loadingSuperLikes,
|
||||||
} = usePlaylistContentState();
|
} = usePlaylistContentState();
|
||||||
|
|
||||||
|
const isScreenSmall = !useMediaQuery(`(min-width:700px)`);
|
||||||
|
|
||||||
return videoData && videoData?.videos?.length === 0 ? (
|
return videoData && videoData?.videos?.length === 0 ? (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -55,197 +63,202 @@ export const PlaylistContent = () => {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
padding: "0px 10px",
|
padding: "0px",
|
||||||
marginLeft: "5%",
|
marginLeft: "2%",
|
||||||
}}
|
}}
|
||||||
onClick={focusVideo}
|
onClick={focusVideo}
|
||||||
>
|
>
|
||||||
<VideoPlayerContainer
|
<VideoPlayerContainer
|
||||||
sx={{
|
sx={{
|
||||||
marginBottom: "30px",
|
width: "100%",
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: isScreenSmall ? "1fr" : "60vw auto",
|
||||||
|
gap: "20px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<>
|
{videoReference && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "grid",
|
aspectRatio: "16/9",
|
||||||
gridTemplateColumns: "55vw 35vw",
|
|
||||||
width: "100vw",
|
|
||||||
gap: "3vw",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{videoReference && (
|
<VideoPlayer
|
||||||
<Box
|
name={videoReference?.name}
|
||||||
sx={{
|
service={videoReference?.service}
|
||||||
aspectRatio: "16/9",
|
identifier={videoReference?.identifier}
|
||||||
}}
|
user={channelName}
|
||||||
>
|
jsonId={id}
|
||||||
<VideoPlayer
|
poster={videoCover || ""}
|
||||||
name={videoReference?.name}
|
nextVideo={nextVideo}
|
||||||
service={videoReference?.service}
|
onEnd={onEndVideo}
|
||||||
identifier={videoReference?.identifier}
|
autoPlay={doAutoPlay}
|
||||||
user={channelName}
|
ref={containerRef}
|
||||||
jsonId={id}
|
videoStyles={{
|
||||||
poster={videoCover || ""}
|
videoContainer: { aspectRatio: "16 / 9" },
|
||||||
nextVideo={nextVideo}
|
video: { aspectRatio: "16 / 9" },
|
||||||
onEnd={onEndVideo}
|
}}
|
||||||
autoPlay={doAutoPlay}
|
/>
|
||||||
ref={containerRef}
|
|
||||||
videoStyles={{
|
|
||||||
videoContainer: { aspectRatio: "16 / 9" },
|
|
||||||
video: { aspectRatio: "16 / 9" },
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{playlistData && (
|
|
||||||
<Playlists
|
|
||||||
playlistData={playlistData}
|
|
||||||
currentVideoIdentifier={videoData?.id}
|
|
||||||
onClick={getVideoData}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<VideoActionsBar
|
|
||||||
channelName={channelName}
|
|
||||||
videoData={videoData}
|
|
||||||
videoReference={videoReference}
|
|
||||||
superLikeList={superLikeList}
|
|
||||||
setSuperLikeList={setSuperLikeList}
|
|
||||||
sx={{ width: "100%" }}
|
|
||||||
/>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "10px",
|
|
||||||
gap: "10px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<VideoTitle
|
|
||||||
variant="h1"
|
|
||||||
color="textPrimary"
|
|
||||||
sx={{
|
|
||||||
textAlign: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{videoData?.title}
|
|
||||||
</VideoTitle>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{videoData?.created && (
|
|
||||||
<Typography
|
|
||||||
variant="h6"
|
|
||||||
sx={{
|
|
||||||
fontSize: "16px",
|
|
||||||
}}
|
|
||||||
color={theme.palette.text.primary}
|
|
||||||
>
|
|
||||||
{formatDate(videoData.created)}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Spacer height="30px" />
|
|
||||||
{videoData?.fullDescription && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
background: "#333333",
|
|
||||||
borderRadius: "5px",
|
|
||||||
padding: "5px",
|
|
||||||
width: "100%",
|
|
||||||
cursor: !descriptionHeight
|
|
||||||
? "default"
|
|
||||||
: isExpandedDescription
|
|
||||||
? "default"
|
|
||||||
: "pointer",
|
|
||||||
position: "relative",
|
|
||||||
}}
|
|
||||||
className={
|
|
||||||
!descriptionHeight
|
|
||||||
? ""
|
|
||||||
: isExpandedDescription
|
|
||||||
? ""
|
|
||||||
: "hover-click"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{descriptionHeight && !isExpandedDescription && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
position: "absolute",
|
|
||||||
top: "0px",
|
|
||||||
right: "0px",
|
|
||||||
left: "0px",
|
|
||||||
bottom: "0px",
|
|
||||||
cursor: "pointer",
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
if (isExpandedDescription) return;
|
|
||||||
setIsExpandedDescription(true);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Box
|
|
||||||
ref={contentRef}
|
|
||||||
sx={{
|
|
||||||
height: !descriptionHeight
|
|
||||||
? "auto"
|
|
||||||
: isExpandedDescription
|
|
||||||
? "auto"
|
|
||||||
: `${descriptionHeight}px`,
|
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{videoData?.htmlDescription ? (
|
|
||||||
<DisplayHtml html={videoData?.htmlDescription} />
|
|
||||||
) : (
|
|
||||||
<VideoDescription
|
|
||||||
variant="body1"
|
|
||||||
color="textPrimary"
|
|
||||||
sx={{
|
|
||||||
cursor: "default",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{videoData?.fullDescription}
|
|
||||||
</VideoDescription>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
{descriptionHeight >= descriptionThreshold && (
|
|
||||||
<Typography
|
|
||||||
onClick={() => {
|
|
||||||
setIsExpandedDescription(prev => !prev);
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
fontWeight: "bold",
|
|
||||||
fontSize: "16px",
|
|
||||||
cursor: "pointer",
|
|
||||||
paddingLeft: "15px",
|
|
||||||
paddingTop: "15px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isExpandedDescription ? "Show less" : "...more"}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
{videoData?.id && videoData?.user && (
|
|
||||||
<SuperLikesSection
|
|
||||||
loadingSuperLikes={loadingSuperLikes}
|
|
||||||
superlikes={superLikeList}
|
|
||||||
postId={videoData?.id || ""}
|
|
||||||
postName={videoData?.user || ""}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{videoData?.id && channelName && (
|
{playlistData && (
|
||||||
<CommentSection
|
<Playlists
|
||||||
postId={videoData?.id || ""}
|
playlistData={playlistData}
|
||||||
postName={channelName || ""}
|
currentVideoIdentifier={videoData?.id}
|
||||||
|
onClick={getVideoData}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</VideoPlayerContainer>
|
</VideoPlayerContainer>
|
||||||
|
<VideoActionsBar
|
||||||
|
channelName={channelName}
|
||||||
|
videoData={videoData}
|
||||||
|
videoReference={videoReference}
|
||||||
|
superLikeList={superLikeList}
|
||||||
|
setSuperLikeList={setSuperLikeList}
|
||||||
|
sx={{ width: "100%" }}
|
||||||
|
/>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
width: "100%",
|
||||||
|
marginTop: "10px",
|
||||||
|
gap: "10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<VideoTitle
|
||||||
|
variant="h1"
|
||||||
|
color="textPrimary"
|
||||||
|
sx={{
|
||||||
|
textAlign: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{videoData?.title}
|
||||||
|
</VideoTitle>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
width: "100%",
|
||||||
|
gap: "20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{videoData?.created && (
|
||||||
|
<Typography
|
||||||
|
variant="h2"
|
||||||
|
sx={{
|
||||||
|
fontSize: fontSizeSmall,
|
||||||
|
}}
|
||||||
|
color={theme.palette.text.primary}
|
||||||
|
>
|
||||||
|
{formatDate(videoData.created)}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
{videoData?.fileSize > minFileSize && (
|
||||||
|
<Typography
|
||||||
|
variant="h1"
|
||||||
|
sx={{
|
||||||
|
fontSize: "90%",
|
||||||
|
}}
|
||||||
|
color={"green"}
|
||||||
|
>
|
||||||
|
{formatBytes(videoData.fileSize, 2, "Decimal")}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<Spacer height="30px" />
|
||||||
|
{videoData?.fullDescription && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
background: "#333333",
|
||||||
|
borderRadius: "5px",
|
||||||
|
padding: "5px",
|
||||||
|
width: "95%",
|
||||||
|
alignSelf: "flex-start",
|
||||||
|
cursor: !descriptionHeight
|
||||||
|
? "default"
|
||||||
|
: isExpandedDescription
|
||||||
|
? "default"
|
||||||
|
: "pointer",
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
!descriptionHeight ? "" : isExpandedDescription ? "" : "hover-click"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{descriptionHeight && !isExpandedDescription && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
top: "0px",
|
||||||
|
right: "0px",
|
||||||
|
left: "0px",
|
||||||
|
bottom: "0px",
|
||||||
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if (isExpandedDescription) return;
|
||||||
|
setIsExpandedDescription(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Box
|
||||||
|
ref={contentRef}
|
||||||
|
sx={{
|
||||||
|
height: !descriptionHeight
|
||||||
|
? "auto"
|
||||||
|
: isExpandedDescription
|
||||||
|
? "auto"
|
||||||
|
: `${descriptionHeight}px`,
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{videoData?.htmlDescription ? (
|
||||||
|
<DisplayHtml html={videoData?.htmlDescription} />
|
||||||
|
) : (
|
||||||
|
<VideoDescription
|
||||||
|
variant="body1"
|
||||||
|
color="textPrimary"
|
||||||
|
sx={{
|
||||||
|
cursor: "default",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{videoData?.fullDescription}
|
||||||
|
</VideoDescription>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
{descriptionHeight >= descriptionThreshold && (
|
||||||
|
<Typography
|
||||||
|
onClick={() => {
|
||||||
|
setIsExpandedDescription(prev => !prev);
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
fontWeight: "bold",
|
||||||
|
fontSize: "16px",
|
||||||
|
cursor: "pointer",
|
||||||
|
paddingLeft: "15px",
|
||||||
|
paddingTop: "15px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isExpandedDescription ? "Show less" : "...more"}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{videoData?.id && videoData?.user && (
|
||||||
|
<SuperLikesSection
|
||||||
|
loadingSuperLikes={loadingSuperLikes}
|
||||||
|
superlikes={superLikeList}
|
||||||
|
postId={videoData?.id || ""}
|
||||||
|
postName={videoData?.user || ""}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{videoData?.id && channelName && (
|
||||||
|
<CommentSection
|
||||||
|
postId={videoData?.id || ""}
|
||||||
|
postName={channelName || ""}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,75 +1,22 @@
|
|||||||
import { Avatar, Box, useTheme } from "@mui/material";
|
import { Box, SxProps, Theme, useMediaQuery } from "@mui/material";
|
||||||
import { FollowButton } from "../../../components/common/ContentButtons/FollowButton.tsx";
|
import { smallScreenSizeString } from "../../../constants/Misc.ts";
|
||||||
import { SubscribeButton } from "../../../components/common/ContentButtons/SubscribeButton.tsx";
|
import { ChannelButtons } from "./ChannelButtons.tsx";
|
||||||
import { RootState } from "../../../state/store.ts";
|
import { ChannelName, ChannelParams } from "./ChannelName.tsx";
|
||||||
import {
|
import { Spacer } from "./VideoContent-styles.tsx";
|
||||||
AuthorTextComment,
|
|
||||||
StyledCardColComment,
|
|
||||||
StyledCardHeaderComment,
|
|
||||||
} from "./VideoContent-styles.tsx";
|
|
||||||
import { useSelector } from "react-redux";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
|
|
||||||
export interface ChannelActionsParams {
|
export const ChannelActions = ({ channelName, sx }: ChannelParams) => {
|
||||||
channelName: string;
|
const boxSX: SxProps<Theme> = {
|
||||||
}
|
display: "flex",
|
||||||
export const ChannelActions = ({ channelName }: ChannelActionsParams) => {
|
flexDirection: "row",
|
||||||
const navigate = useNavigate();
|
flexWrap: "wrap",
|
||||||
const theme = useTheme();
|
rowGap: "10px",
|
||||||
const userName = useSelector((state: RootState) => state.auth.user?.name);
|
columnGap: "20px",
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box sx={{ ...boxSX, ...sx }}>
|
||||||
<StyledCardHeaderComment
|
<ChannelName channelName={channelName} />
|
||||||
sx={{
|
<ChannelButtons channelName={channelName} />
|
||||||
"& .MuiCardHeader-content": {
|
|
||||||
overflow: "hidden",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
cursor: "pointer",
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
navigate(`/channel/${channelName}`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Avatar
|
|
||||||
src={`/arbitrary/THUMBNAIL/${channelName}/qortal_avatar`}
|
|
||||||
alt={`${channelName}'s avatar`}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<StyledCardColComment>
|
|
||||||
<AuthorTextComment
|
|
||||||
color={
|
|
||||||
theme.palette.mode === "light"
|
|
||||||
? theme.palette.text.secondary
|
|
||||||
: "#d6e8ff"
|
|
||||||
}
|
|
||||||
sx={{
|
|
||||||
cursor: "pointer",
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
navigate(`/channel/${channelName}`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{channelName}
|
|
||||||
{channelName !== userName && (
|
|
||||||
<>
|
|
||||||
<SubscribeButton
|
|
||||||
subscriberName={channelName}
|
|
||||||
sx={{ marginLeft: "20px" }}
|
|
||||||
/>
|
|
||||||
<FollowButton
|
|
||||||
followerName={channelName}
|
|
||||||
sx={{ marginLeft: "20px" }}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</AuthorTextComment>
|
|
||||||
</StyledCardColComment>
|
|
||||||
</StyledCardHeaderComment>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
24
src/pages/ContentPages/VideoContent/ChannelButtons.tsx
Normal file
24
src/pages/ContentPages/VideoContent/ChannelButtons.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { FollowButton } from "../../../components/common/ContentButtons/FollowButton.tsx";
|
||||||
|
import { SubscribeButton } from "../../../components/common/ContentButtons/SubscribeButton.tsx";
|
||||||
|
import { RootState } from "../../../state/store.ts";
|
||||||
|
import { ChannelParams } from "./ChannelName.tsx";
|
||||||
|
import { StyledCardColComment } from "./VideoContent-styles.tsx";
|
||||||
|
|
||||||
|
export const ChannelButtons = ({ channelName, sx }: ChannelParams) => {
|
||||||
|
const userName = useSelector((state: RootState) => state.auth.user?.name);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledCardColComment sx={{ alignItems: "center", ...sx }}>
|
||||||
|
{channelName !== userName && (
|
||||||
|
<>
|
||||||
|
<SubscribeButton subscriberName={channelName} />
|
||||||
|
<FollowButton
|
||||||
|
followerName={channelName}
|
||||||
|
sx={{ marginLeft: "20px" }}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</StyledCardColComment>
|
||||||
|
);
|
||||||
|
};
|
58
src/pages/ContentPages/VideoContent/ChannelName.tsx
Normal file
58
src/pages/ContentPages/VideoContent/ChannelName.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { Avatar, Box, SxProps, Theme, useTheme } from "@mui/material";
|
||||||
|
import {
|
||||||
|
AuthorTextComment,
|
||||||
|
StyledCardHeaderComment,
|
||||||
|
} from "./VideoContent-styles.tsx";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
export interface ChannelParams {
|
||||||
|
channelName: string;
|
||||||
|
sx?: SxProps<Theme>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ChannelName = ({ channelName }: ChannelParams) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledCardHeaderComment
|
||||||
|
sx={{
|
||||||
|
"& .MuiCardHeader-content": {
|
||||||
|
overflow: "hidden",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
navigate(`/channel/${channelName}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
src={`/arbitrary/THUMBNAIL/${channelName}/qortal_avatar`}
|
||||||
|
alt={`${channelName}'s avatar`}
|
||||||
|
/>
|
||||||
|
<AuthorTextComment
|
||||||
|
color={
|
||||||
|
theme.palette.mode === "light"
|
||||||
|
? theme.palette.text.secondary
|
||||||
|
: "#d6e8ff"
|
||||||
|
}
|
||||||
|
sx={{
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "inline",
|
||||||
|
marginLeft: "10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{channelName}
|
||||||
|
</AuthorTextComment>
|
||||||
|
</Box>
|
||||||
|
</StyledCardHeaderComment>
|
||||||
|
);
|
||||||
|
};
|
@ -1,25 +1,18 @@
|
|||||||
import { Avatar, Box, SxProps, Theme, useTheme } from "@mui/material";
|
import DownloadIcon from "@mui/icons-material/Download";
|
||||||
import { FollowButton } from "../../../components/common/ContentButtons/FollowButton.tsx";
|
import { Box, SxProps, Theme, useMediaQuery } from "@mui/material";
|
||||||
|
import { useMemo } from "react";
|
||||||
import { LikeAndDislike } from "../../../components/common/ContentButtons/LikeAndDislike.tsx";
|
import { LikeAndDislike } from "../../../components/common/ContentButtons/LikeAndDislike.tsx";
|
||||||
import { SubscribeButton } from "../../../components/common/ContentButtons/SubscribeButton.tsx";
|
|
||||||
import { SuperLike } from "../../../components/common/ContentButtons/SuperLike.tsx";
|
import { SuperLike } from "../../../components/common/ContentButtons/SuperLike.tsx";
|
||||||
import FileElement from "../../../components/common/FileElement.tsx";
|
import FileElement from "../../../components/common/FileElement.tsx";
|
||||||
import { videoRefType } from "../../../components/common/VideoPlayer/VideoPlayer.tsx";
|
import {
|
||||||
import { titleFormatterOnSave } from "../../../constants/Misc.ts";
|
smallScreenSizeString,
|
||||||
import { useFetchSuperLikes } from "../../../hooks/useFetchSuperLikes.tsx";
|
titleFormatterOnSave,
|
||||||
import DownloadIcon from "@mui/icons-material/Download";
|
} from "../../../constants/Misc.ts";
|
||||||
import { RootState } from "../../../state/store.ts";
|
|
||||||
import { ChannelActions } from "./ChannelActions.tsx";
|
import { ChannelActions } from "./ChannelActions.tsx";
|
||||||
import {
|
import {
|
||||||
AuthorTextComment,
|
|
||||||
FileAttachmentContainer,
|
FileAttachmentContainer,
|
||||||
FileAttachmentFont,
|
FileAttachmentFont,
|
||||||
StyledCardColComment,
|
|
||||||
StyledCardHeaderComment,
|
|
||||||
} from "./VideoContent-styles.tsx";
|
} from "./VideoContent-styles.tsx";
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
import { useSelector } from "react-redux";
|
|
||||||
import { useMemo, useState } from "react";
|
|
||||||
|
|
||||||
export interface VideoActionsBarProps {
|
export interface VideoActionsBarProps {
|
||||||
channelName: string;
|
channelName: string;
|
||||||
@ -82,11 +75,12 @@ export const VideoActionsBar = ({
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "80%",
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: "1fr 1fr",
|
|
||||||
marginTop: "15px",
|
marginTop: "15px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
gap: "20px",
|
||||||
...sx,
|
...sx,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -95,6 +89,8 @@ export const VideoActionsBar = ({
|
|||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
|
height: "100%",
|
||||||
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{videoData && (
|
{videoData && (
|
||||||
@ -110,28 +106,30 @@ export const VideoActionsBar = ({
|
|||||||
setSuperLikeList(prev => [val, ...prev]);
|
setSuperLikeList(prev => [val, ...prev]);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FileAttachmentContainer>
|
|
||||||
<FileAttachmentFont>Save to Disk</FileAttachmentFont>
|
|
||||||
<FileElement
|
|
||||||
fileInfo={{
|
|
||||||
...videoReference,
|
|
||||||
filename: saveAsFilename,
|
|
||||||
mimeType: videoData?.videoType || '"video/mp4',
|
|
||||||
}}
|
|
||||||
title={videoData?.filename || videoData?.title?.slice(0, 20)}
|
|
||||||
customStyles={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DownloadIcon />
|
|
||||||
</FileElement>
|
|
||||||
</FileAttachmentContainer>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
{videoData && (
|
||||||
|
<FileAttachmentContainer sx={{ width: "100%", maxWidth: "340px" }}>
|
||||||
|
<FileAttachmentFont>Save Video</FileAttachmentFont>
|
||||||
|
<FileElement
|
||||||
|
fileInfo={{
|
||||||
|
...videoReference,
|
||||||
|
filename: saveAsFilename,
|
||||||
|
mimeType: videoData?.videoType || '"video/mp4',
|
||||||
|
}}
|
||||||
|
title={videoData?.filename || videoData?.title?.slice(0, 20)}
|
||||||
|
customStyles={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DownloadIcon />
|
||||||
|
</FileElement>
|
||||||
|
</FileAttachmentContainer>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { styled } from "@mui/system";
|
import { styled } from "@mui/system";
|
||||||
import { Box, Grid, Typography, Checkbox } from "@mui/material";
|
import { Box, Grid, Typography, Checkbox } from "@mui/material";
|
||||||
import { fontSizeMedium } from "../../../constants/Misc.ts";
|
import { fontSizeMedium, fontSizeSmall } from "../../../constants/Misc.ts";
|
||||||
|
import { useIsMobile } from "../../../hooks/useIsMobile.ts";
|
||||||
|
|
||||||
export const VideoContentContainer = styled(Box)(({ theme }) => ({
|
export const VideoContentContainer = styled(Box)(({ theme }) => ({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -8,10 +9,7 @@ export const VideoContentContainer = styled(Box)(({ theme }) => ({
|
|||||||
alignItems: "start",
|
alignItems: "start",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const VideoPlayerContainer = styled(Box)(({ theme }) => ({
|
export const VideoPlayerContainer = styled(Box)(({ theme }) => ({}));
|
||||||
width: "55vw",
|
|
||||||
marginLeft: "5%",
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const VideoTitle = styled(Typography)(({ theme }) => ({
|
export const VideoTitle = styled(Typography)(({ theme }) => ({
|
||||||
fontFamily: "Raleway",
|
fontFamily: "Raleway",
|
||||||
@ -57,14 +55,14 @@ export const StyledCardCol = styled(Box)({
|
|||||||
export const StyledCardColComment = styled(Box)({
|
export const StyledCardColComment = styled(Box)({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
flexDirection: "column",
|
flexDirection: "row",
|
||||||
gap: "2px",
|
gap: "2px",
|
||||||
alignItems: "flex-start",
|
alignItems: "flex-start",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const AuthorTextComment = styled(Typography)({
|
export const AuthorTextComment = styled(Typography)({
|
||||||
fontFamily: "Raleway, sans-serif",
|
fontFamily: "Raleway, sans-serif",
|
||||||
fontSize: "20px",
|
fontSize: fontSizeMedium,
|
||||||
lineHeight: "1.2",
|
lineHeight: "1.2",
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -73,7 +71,6 @@ export const FileAttachmentContainer = styled(Box)(({ theme }) => ({
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
padding: "5px 10px",
|
padding: "5px 10px",
|
||||||
border: `1px solid ${theme.palette.text.primary}`,
|
border: `1px solid ${theme.palette.text.primary}`,
|
||||||
width: "350px",
|
|
||||||
height: "50px",
|
height: "50px",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
import { Box, Typography } from "@mui/material";
|
import { Box, Typography, useMediaQuery } from "@mui/material";
|
||||||
import React from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
import DeletedVideo from "../../../assets/img/DeletedVideo.jpg";
|
import DeletedVideo from "../../../assets/img/DeletedVideo.jpg";
|
||||||
import { CommentSection } from "../../../components/common/Comments/CommentSection.tsx";
|
import { CommentSection } from "../../../components/common/Comments/CommentSection.tsx";
|
||||||
import { SuperLikesSection } from "../../../components/common/SuperLikesList/SuperLikesSection.tsx";
|
import { SuperLikesSection } from "../../../components/common/SuperLikesList/SuperLikesSection.tsx";
|
||||||
import { DisplayHtml } from "../../../components/common/TextEditor/DisplayHtml.tsx";
|
import { DisplayHtml } from "../../../components/common/TextEditor/DisplayHtml.tsx";
|
||||||
import { VideoPlayer } from "../../../components/common/VideoPlayer/VideoPlayer.tsx";
|
import { VideoPlayer } from "../../../components/common/VideoPlayer/VideoPlayer.tsx";
|
||||||
import { minFileSize } from "../../../constants/Misc.ts";
|
import {
|
||||||
|
fontSizeSmall,
|
||||||
|
largeScreenSizeString,
|
||||||
|
minFileSize,
|
||||||
|
smallScreenSizeString,
|
||||||
|
} from "../../../constants/Misc.ts";
|
||||||
|
import { useIsMobile } from "../../../hooks/useIsMobile.ts";
|
||||||
import { formatBytes } from "../../../utils/numberFunctions.ts";
|
import { formatBytes } from "../../../utils/numberFunctions.ts";
|
||||||
import { formatDate } from "../../../utils/time.ts";
|
import { formatDate } from "../../../utils/time.ts";
|
||||||
import { VideoActionsBar } from "./VideoActionsBar.tsx";
|
import { VideoActionsBar } from "./VideoActionsBar.tsx";
|
||||||
@ -18,6 +24,7 @@ import {
|
|||||||
VideoPlayerContainer,
|
VideoPlayerContainer,
|
||||||
VideoTitle,
|
VideoTitle,
|
||||||
} from "./VideoContent-styles.tsx";
|
} from "./VideoContent-styles.tsx";
|
||||||
|
import { useSignal } from "@preact/signals-react";
|
||||||
|
|
||||||
export const VideoContent = () => {
|
export const VideoContent = () => {
|
||||||
const {
|
const {
|
||||||
@ -39,18 +46,47 @@ export const VideoContent = () => {
|
|||||||
superLikeList,
|
superLikeList,
|
||||||
setSuperLikeList,
|
setSuperLikeList,
|
||||||
} = useVideoContentState();
|
} = useVideoContentState();
|
||||||
|
|
||||||
|
const isScreenSmall = !useMediaQuery(`(min-width:${smallScreenSizeString})`);
|
||||||
|
const [screenWidth, setScreenWidth] = useState<number>(
|
||||||
|
window.innerWidth + 120
|
||||||
|
);
|
||||||
|
let videoWidth = 100;
|
||||||
|
const maxWidth = 95;
|
||||||
|
const pixelsPerPercent = 17.5;
|
||||||
|
const smallScreenPixels = 700;
|
||||||
|
|
||||||
|
if (!isScreenSmall)
|
||||||
|
videoWidth =
|
||||||
|
maxWidth - (screenWidth - smallScreenPixels) / pixelsPerPercent;
|
||||||
|
|
||||||
|
const minWidthPercent = 70;
|
||||||
|
if (videoWidth < minWidthPercent) videoWidth = minWidthPercent;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener("resize", e => {
|
||||||
|
setScreenWidth(window.innerWidth + 120);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
padding: "0px 10px 0px 5%",
|
padding: `0px 0px 0px ${isScreenSmall ? "5px" : "2%"}`,
|
||||||
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
onClick={focusVideo}
|
onClick={focusVideo}
|
||||||
>
|
>
|
||||||
{videoReference ? (
|
{videoReference ? (
|
||||||
<VideoPlayerContainer>
|
<VideoPlayerContainer
|
||||||
|
sx={{
|
||||||
|
width: `${videoWidth}%`,
|
||||||
|
marginLeft: "0%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<VideoPlayer
|
<VideoPlayer
|
||||||
name={videoReference?.name}
|
name={videoReference?.name}
|
||||||
service={videoReference?.service}
|
service={videoReference?.service}
|
||||||
@ -77,64 +113,61 @@ export const VideoContent = () => {
|
|||||||
<Box sx={{ width: "55vw", aspectRatio: "16/9" }}></Box>
|
<Box sx={{ width: "55vw", aspectRatio: "16/9" }}></Box>
|
||||||
)}
|
)}
|
||||||
<VideoContentContainer>
|
<VideoContentContainer>
|
||||||
|
<VideoTitle
|
||||||
|
variant={isScreenSmall ? "h2" : "h1"}
|
||||||
|
color="textPrimary"
|
||||||
|
sx={{
|
||||||
|
textAlign: "start",
|
||||||
|
marginTop: "10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{videoData?.title}
|
||||||
|
</VideoTitle>
|
||||||
|
<Box>
|
||||||
|
{videoData?.created && (
|
||||||
|
<Typography
|
||||||
|
variant="h2"
|
||||||
|
sx={{
|
||||||
|
fontSize: fontSizeSmall,
|
||||||
|
display: "inline",
|
||||||
|
}}
|
||||||
|
color={theme.palette.text.primary}
|
||||||
|
>
|
||||||
|
{formatDate(videoData.created)}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{videoData?.fileSize > minFileSize && (
|
||||||
|
<Typography
|
||||||
|
variant="h1"
|
||||||
|
sx={{
|
||||||
|
fontSize: "90%",
|
||||||
|
display: "inline",
|
||||||
|
marginLeft: "20px",
|
||||||
|
}}
|
||||||
|
color={"green"}
|
||||||
|
>
|
||||||
|
{formatBytes(videoData.fileSize, 2, "Decimal")}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
<VideoActionsBar
|
<VideoActionsBar
|
||||||
channelName={channelName}
|
channelName={channelName}
|
||||||
videoData={videoData}
|
videoData={videoData}
|
||||||
setSuperLikeList={setSuperLikeList}
|
setSuperLikeList={setSuperLikeList}
|
||||||
superLikeList={superLikeList}
|
superLikeList={superLikeList}
|
||||||
videoReference={videoReference}
|
videoReference={videoReference}
|
||||||
|
sx={{ width: "calc(100% - 5px)" }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Box
|
<Spacer height="15px" />
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "20px",
|
|
||||||
gap: "10px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<VideoTitle
|
|
||||||
variant="h1"
|
|
||||||
color="textPrimary"
|
|
||||||
sx={{
|
|
||||||
textAlign: "start",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{videoData?.title}
|
|
||||||
</VideoTitle>
|
|
||||||
</Box>
|
|
||||||
{videoData?.fileSize > minFileSize && (
|
|
||||||
<Typography
|
|
||||||
variant="h1"
|
|
||||||
sx={{
|
|
||||||
fontSize: "90%",
|
|
||||||
}}
|
|
||||||
color={"green"}
|
|
||||||
>
|
|
||||||
{formatBytes(videoData.fileSize, 2, "Decimal")}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
{videoData?.created && (
|
|
||||||
<Typography
|
|
||||||
variant="h6"
|
|
||||||
sx={{
|
|
||||||
fontSize: "16px",
|
|
||||||
}}
|
|
||||||
color={theme.palette.text.primary}
|
|
||||||
>
|
|
||||||
{formatDate(videoData.created)}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
<Spacer height="30px" />
|
|
||||||
{videoData?.fullDescription && (
|
{videoData?.fullDescription && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
background: "#333333",
|
background: "#333333",
|
||||||
borderRadius: "5px",
|
borderRadius: "5px",
|
||||||
padding: "5px",
|
padding: "5px",
|
||||||
width: "70%",
|
width: "95%",
|
||||||
cursor: !descriptionHeight
|
cursor: !descriptionHeight
|
||||||
? "default"
|
? "default"
|
||||||
: isExpandedDescription
|
: isExpandedDescription
|
||||||
|
@ -7,9 +7,11 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
OutlinedInput,
|
OutlinedInput,
|
||||||
Select,
|
Select,
|
||||||
|
useMediaQuery,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { StatsData } from "../../../components/StatsData.tsx";
|
import { StatsData } from "../../../components/StatsData.tsx";
|
||||||
import { categories, subCategories } from "../../../constants/Categories.ts";
|
import { categories, subCategories } from "../../../constants/Categories.ts";
|
||||||
|
import { smallScreenSizeString } from "../../../constants/Misc.ts";
|
||||||
import { useSidebarState } from "./SearchSidebar-State.ts";
|
import { useSidebarState } from "./SearchSidebar-State.ts";
|
||||||
import {
|
import {
|
||||||
FiltersCol,
|
FiltersCol,
|
||||||
@ -38,8 +40,19 @@ export const SearchSidebar = ({ onSearch }: SearchSidebarProps) => {
|
|||||||
filtersToDefault,
|
filtersToDefault,
|
||||||
} = useSidebarState(onSearch);
|
} = useSidebarState(onSearch);
|
||||||
|
|
||||||
|
const filtersStyle = { width: "75px", marginRight: "10px" };
|
||||||
|
const isScreenSmall = !useMediaQuery(`(min-width:600px)`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FiltersCol item xs={12} md={2} lg={2} xl={2} sm={3}>
|
<Box
|
||||||
|
sx={{
|
||||||
|
marginLeft: "5px",
|
||||||
|
marginRight: isScreenSmall ? "5px" : "0px",
|
||||||
|
alignItems: "center",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<FiltersContainer>
|
<FiltersContainer>
|
||||||
<StatsData />
|
<StatsData />
|
||||||
<Input
|
<Input
|
||||||
@ -101,12 +114,11 @@ export const SearchSidebar = ({ onSearch }: SearchSidebarProps) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<FiltersSubContainer>
|
<FiltersSubContainer>
|
||||||
<FormControl sx={{ width: "100%", marginTop: "30px" }}>
|
<FormControl sx={{ width: "98%", marginTop: "30px" }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
gap: "20px",
|
gap: "20px",
|
||||||
alignItems: "center",
|
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -201,7 +213,7 @@ export const SearchSidebar = ({ onSearch }: SearchSidebarProps) => {
|
|||||||
</FiltersSubContainer>
|
</FiltersSubContainer>
|
||||||
<FiltersSubContainer>
|
<FiltersSubContainer>
|
||||||
<FiltersRow>
|
<FiltersRow>
|
||||||
Videos
|
<span style={filtersStyle}>Videos</span>
|
||||||
<FiltersRadioButton
|
<FiltersRadioButton
|
||||||
checked={filterType === "videos"}
|
checked={filterType === "videos"}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
@ -211,7 +223,7 @@ export const SearchSidebar = ({ onSearch }: SearchSidebarProps) => {
|
|||||||
/>
|
/>
|
||||||
</FiltersRow>
|
</FiltersRow>
|
||||||
<FiltersRow>
|
<FiltersRow>
|
||||||
Playlists
|
<span style={filtersStyle}> Playlists</span>
|
||||||
<FiltersRadioButton
|
<FiltersRadioButton
|
||||||
checked={filterType === "playlists"}
|
checked={filterType === "playlists"}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
@ -221,12 +233,15 @@ export const SearchSidebar = ({ onSearch }: SearchSidebarProps) => {
|
|||||||
/>
|
/>
|
||||||
</FiltersRow>
|
</FiltersRow>
|
||||||
</FiltersSubContainer>
|
</FiltersSubContainer>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
filtersToDefault();
|
filtersToDefault();
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
marginTop: "20px",
|
marginTop: "20px",
|
||||||
|
width: "80%",
|
||||||
|
alignSelf: "center",
|
||||||
}}
|
}}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
@ -238,12 +253,14 @@ export const SearchSidebar = ({ onSearch }: SearchSidebarProps) => {
|
|||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
marginTop: "20px",
|
marginTop: "20px",
|
||||||
|
width: "80%",
|
||||||
|
alignSelf: "center",
|
||||||
}}
|
}}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
>
|
>
|
||||||
Search
|
Search
|
||||||
</Button>
|
</Button>
|
||||||
</FiltersContainer>
|
</FiltersContainer>
|
||||||
</FiltersCol>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
Autocomplete,
|
Autocomplete,
|
||||||
Radio,
|
Radio,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
import { fontSizeMedium, fontSizeSmall } from "../../../constants/Misc.ts";
|
||||||
|
|
||||||
export const VideoContainer = styled(Grid)(({ theme }) => ({
|
export const VideoContainer = styled(Grid)(({ theme }) => ({
|
||||||
position: "relative",
|
position: "relative",
|
||||||
@ -23,7 +24,7 @@ export const VideoContainer = styled(Grid)(({ theme }) => ({
|
|||||||
|
|
||||||
export const VideoCardContainer = styled("div")(({ theme }) => ({
|
export const VideoCardContainer = styled("div")(({ theme }) => ({
|
||||||
display: "grid",
|
display: "grid",
|
||||||
gridTemplateColumns: "repeat(auto-fill, minmax(250px, 1fr))",
|
gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))",
|
||||||
gap: theme.spacing(2),
|
gap: theme.spacing(2),
|
||||||
padding: "10px",
|
padding: "10px",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@ -31,7 +32,7 @@ export const VideoCardContainer = styled("div")(({ theme }) => ({
|
|||||||
|
|
||||||
export const VideoCardCol = styled("div")({
|
export const VideoCardCol = styled("div")({
|
||||||
position: "relative",
|
position: "relative",
|
||||||
minWidth: "250px", // Minimum width of each item
|
minWidth: "200px", // Minimum width of each item
|
||||||
maxWidth: "1fr", // Maximum width, allowing the item to fill the column
|
maxWidth: "1fr", // Maximum width, allowing the item to fill the column
|
||||||
// ... other styles
|
// ... other styles
|
||||||
});
|
});
|
||||||
@ -132,16 +133,14 @@ export const FiltersCol = styled(Grid)(({ theme }) => ({
|
|||||||
export const FiltersContainer = styled(Box)(({ theme }) => ({
|
export const FiltersContainer = styled(Box)(({ theme }) => ({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
justifyContent: "space-between",
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const FiltersRow = styled(Box)(({ theme }) => ({
|
export const FiltersRow = styled(Box)(({ theme }) => ({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "space-between",
|
justifyContent: "center",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
padding: "0 15px",
|
fontSize: fontSizeSmall,
|
||||||
fontSize: "16px",
|
|
||||||
userSelect: "none",
|
userSelect: "none",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -161,7 +160,7 @@ export const FiltersRadioButton = styled(Radio)(({ theme }) => ({
|
|||||||
|
|
||||||
export const FiltersSubContainer = styled(Box)(({ theme }) => ({
|
export const FiltersSubContainer = styled(Box)(({ theme }) => ({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "start",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: "5px",
|
gap: "5px",
|
||||||
}));
|
}));
|
||||||
|
@ -145,7 +145,6 @@ export const VideoList = ({ videos }: VideoListProps) => {
|
|||||||
maxHeight: "50%",
|
maxHeight: "50%",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<VideoCardTitle>{videoObj?.title}</VideoCardTitle>
|
<VideoCardTitle>{videoObj?.title}</VideoCardTitle>
|
||||||
<BottomParent>
|
<BottomParent>
|
||||||
<NameContainer
|
<NameContainer
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
import { TabContext, TabList, TabPanel } from "@mui/lab";
|
import { TabContext, TabList, TabPanel } from "@mui/lab";
|
||||||
|
|
||||||
import { Box, Grid, Tab } from "@mui/material";
|
import { Box, Grid, Tab, useMediaQuery } from "@mui/material";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import LazyLoad from "../../components/common/LazyLoad";
|
import LazyLoad from "../../components/common/LazyLoad";
|
||||||
import { ListSuperLikeContainer } from "../../components/common/ListSuperLikes/ListSuperLikeContainer.tsx";
|
import { ListSuperLikeContainer } from "../../components/common/ListSuperLikes/ListSuperLikeContainer.tsx";
|
||||||
import { fontSizeMedium } from "../../constants/Misc.ts";
|
import {
|
||||||
|
fontSizeLarge,
|
||||||
|
fontSizeMedium,
|
||||||
|
fontSizeSmall,
|
||||||
|
} from "../../constants/Misc.ts";
|
||||||
|
import { useIsMobile } from "../../hooks/useIsMobile.ts";
|
||||||
import { SearchSidebar } from "./Components/SearchSidebar.tsx";
|
import { SearchSidebar } from "./Components/SearchSidebar.tsx";
|
||||||
import { FiltersCol, VideoManagerRow } from "./Components/VideoList-styles.tsx";
|
import { FiltersCol, VideoManagerRow } from "./Components/VideoList-styles.tsx";
|
||||||
import VideoList from "./Components/VideoList.tsx";
|
import VideoList from "./Components/VideoList.tsx";
|
||||||
@ -29,79 +34,77 @@ export const Home = ({ mode }: HomeProps) => {
|
|||||||
paddingLeft: "0px",
|
paddingLeft: "0px",
|
||||||
paddingRight: "0px",
|
paddingRight: "0px",
|
||||||
};
|
};
|
||||||
|
const isScreenSmall = !useMediaQuery("(min-width:600px)");
|
||||||
|
const isScreenLarge = useMediaQuery("(min-width:1200px)");
|
||||||
|
|
||||||
|
const tabSX = {
|
||||||
|
fontSize: isScreenSmall ? fontSizeSmall : fontSizeLarge,
|
||||||
|
paddingLeft: "0px",
|
||||||
|
paddingRight: "0px",
|
||||||
|
};
|
||||||
|
|
||||||
|
const homeBaseSX = { display: "grid", width: "100%" };
|
||||||
|
const bigGridSX = { gridTemplateColumns: "200px auto 250px" };
|
||||||
|
const mediumGridSX = { gridTemplateColumns: "200px auto" };
|
||||||
|
const smallGridSX = { gridTemplateColumns: "100%", gap: "20px" };
|
||||||
|
|
||||||
|
let homeColumns: object;
|
||||||
|
if (isScreenLarge) homeColumns = bigGridSX;
|
||||||
|
else if (!isScreenSmall) homeColumns = mediumGridSX;
|
||||||
|
else homeColumns = smallGridSX;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid container sx={{ width: "100%" }}>
|
<Box sx={{ ...homeBaseSX, ...homeColumns }}>
|
||||||
<SearchSidebar onSearch={getVideosHandler} />
|
<SearchSidebar onSearch={getVideosHandler} />
|
||||||
<Grid item xs={12} md={10} lg={7} xl={8} sm={9}>
|
|
||||||
<VideoManagerRow>
|
<Box
|
||||||
<Box
|
sx={{
|
||||||
sx={{
|
width: "100%",
|
||||||
width: "100%",
|
display: "flex",
|
||||||
display: "flex",
|
flexDirection: "column",
|
||||||
flexDirection: "column",
|
alignItems: "center",
|
||||||
alignItems: "center",
|
}}
|
||||||
marginTop: "20px",
|
>
|
||||||
}}
|
<TabContext value={tabValue}>
|
||||||
|
<TabList
|
||||||
|
onChange={changeTab}
|
||||||
|
textColor={"secondary"}
|
||||||
|
indicatorColor={"secondary"}
|
||||||
|
centered={false}
|
||||||
>
|
>
|
||||||
<SubtitleContainer
|
<Tab label="All" value={"all"} sx={tabSX} />
|
||||||
sx={{
|
<Tab label="Subscriptions" value={"subscriptions"} sx={tabSX} />
|
||||||
justifyContent: "flex-start",
|
</TabList>
|
||||||
paddingLeft: "15px",
|
<TabPanel value={"all"} sx={tabPaneSX}>
|
||||||
width: "100%",
|
<VideoList videos={videos} />
|
||||||
maxWidth: "1400px",
|
<LazyLoad
|
||||||
}}
|
onLoadMore={getVideosHandler}
|
||||||
></SubtitleContainer>
|
isLoading={isLoading}
|
||||||
<TabContext value={tabValue}>
|
></LazyLoad>
|
||||||
<TabList
|
</TabPanel>
|
||||||
onChange={changeTab}
|
<TabPanel value={"subscriptions"} sx={tabPaneSX}>
|
||||||
textColor={"secondary"}
|
{filteredSubscriptionList.length > 0 ? (
|
||||||
indicatorColor={"secondary"}
|
<>
|
||||||
>
|
|
||||||
<Tab
|
|
||||||
label="All Videos"
|
|
||||||
value={"all"}
|
|
||||||
sx={{ fontSize: fontSizeMedium }}
|
|
||||||
/>
|
|
||||||
<Tab
|
|
||||||
label="Subscriptions"
|
|
||||||
value={"subscriptions"}
|
|
||||||
sx={{ fontSize: fontSizeMedium }}
|
|
||||||
/>
|
|
||||||
</TabList>
|
|
||||||
<TabPanel value={"all"} sx={tabPaneSX}>
|
|
||||||
<VideoList videos={videos} />
|
<VideoList videos={videos} />
|
||||||
<LazyLoad
|
<LazyLoad
|
||||||
onLoadMore={getVideosHandler}
|
onLoadMore={getVideosHandler}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
></LazyLoad>
|
></LazyLoad>
|
||||||
</TabPanel>
|
</>
|
||||||
<TabPanel value={"subscriptions"} sx={tabPaneSX}>
|
) : (
|
||||||
{filteredSubscriptionList.length > 0 ? (
|
!isLoading && (
|
||||||
<>
|
<div style={{ textAlign: "center" }}>
|
||||||
<VideoList videos={videos} />
|
You have no subscriptions
|
||||||
<LazyLoad
|
</div>
|
||||||
onLoadMore={getVideosHandler}
|
)
|
||||||
isLoading={isLoading}
|
)}
|
||||||
></LazyLoad>
|
</TabPanel>
|
||||||
</>
|
</TabContext>
|
||||||
) : !isLoading ? (
|
</Box>
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
You have no subscriptions
|
<ListSuperLikeContainer />
|
||||||
</div>
|
</Box>
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
|
||||||
</TabPanel>
|
|
||||||
</TabContext>
|
|
||||||
</Box>
|
|
||||||
</VideoManagerRow>
|
|
||||||
</Grid>
|
|
||||||
<FiltersCol item xs={0} lg={3} xl={2}>
|
|
||||||
<ListSuperLikeContainer />
|
|
||||||
</FiltersCol>
|
|
||||||
</Grid>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -19,6 +19,8 @@ interface settingsState {
|
|||||||
subscriptionListFilter: SubscriptionListFilterType;
|
subscriptionListFilter: SubscriptionListFilterType;
|
||||||
showStats: boolean;
|
showStats: boolean;
|
||||||
volume: number;
|
volume: number;
|
||||||
|
mutedVolume: number;
|
||||||
|
isMuted: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: settingsState = {
|
const initialState: settingsState = {
|
||||||
@ -30,6 +32,8 @@ const initialState: settingsState = {
|
|||||||
subscriptionListFilter: "currentNameOnly",
|
subscriptionListFilter: "currentNameOnly",
|
||||||
showStats: true,
|
showStats: true,
|
||||||
volume: 0.5,
|
volume: 0.5,
|
||||||
|
mutedVolume: 0,
|
||||||
|
isMuted: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const persistSlice = createSlice({
|
export const persistSlice = createSlice({
|
||||||
@ -77,6 +81,12 @@ export const persistSlice = createSlice({
|
|||||||
setVolumeSetting: (state, action: PayloadAction<number>) => {
|
setVolumeSetting: (state, action: PayloadAction<number>) => {
|
||||||
state.volume = action.payload;
|
state.volume = action.payload;
|
||||||
},
|
},
|
||||||
|
setMutedVolumeSetting: (state, action: PayloadAction<number>) => {
|
||||||
|
state.mutedVolume = action.payload;
|
||||||
|
},
|
||||||
|
setIsMuted: (state, action: PayloadAction<boolean>) => {
|
||||||
|
state.isMuted = action.payload;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -89,6 +99,8 @@ export const {
|
|||||||
changeFilterType,
|
changeFilterType,
|
||||||
resetSubscriptions,
|
resetSubscriptions,
|
||||||
setVolumeSetting,
|
setVolumeSetting,
|
||||||
|
setMutedVolumeSetting,
|
||||||
|
setIsMuted,
|
||||||
} = persistSlice.actions;
|
} = persistSlice.actions;
|
||||||
|
|
||||||
export default persistSlice.reducer;
|
export default persistSlice.reducer;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user