mirror of
https://github.com/Qortal/q-tube.git
synced 2025-02-11 17:55:51 +00:00
Mobile support for Home, Video, and Channel pages.
DownloadTaskManager.tsx: 1. Fixed bug that made clicking on videos do nothing instead of going to that video's page. 2. It also displays the video title instead of identifier. 3. The menu is a lot wider, so it is far easier to see the full titles of videos VideoPlayer.tsx now persists across sessions whether it was muted and its volume when unmuting. When screen is small, VideoPlayer automatically switches to Mobile Controls. Mobile Controls now show Playback Rate and Fullscreen buttons. DownloadTaskManager.tsx now shows the video title instead of identifier.
This commit is contained in:
parent
da0747463b
commit
49a232136d
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "qtube",
|
||||
"private": true,
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
@ -1,10 +1,11 @@
|
||||
import React from "react";
|
||||
import { smallScreenSizeString } from "../../constants/Misc.ts";
|
||||
import { CardContentContainerComment } from "../common/Comments/Comments-styles";
|
||||
import {
|
||||
CrowdfundSubTitle,
|
||||
CrowdfundSubTitleRow,
|
||||
} 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";
|
||||
|
||||
export const Playlists = ({
|
||||
@ -13,7 +14,8 @@ export const Playlists = ({
|
||||
onClick,
|
||||
}) => {
|
||||
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 (
|
||||
<Box
|
||||
@ -21,7 +23,7 @@ export const Playlists = ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
maxHeight: "30.94vw",
|
||||
height: isScreenSmall ? "200px" : videoPlayerHeight,
|
||||
}}
|
||||
>
|
||||
<CardContentContainerComment
|
||||
@ -32,7 +34,7 @@ export const Playlists = ({
|
||||
}}
|
||||
>
|
||||
{playlistData?.videos?.map((vid, index) => {
|
||||
const isCurrentVidPlayling =
|
||||
const isCurrentVidPlaying =
|
||||
vid?.identifier === currentVideoIdentifier;
|
||||
|
||||
return (
|
||||
@ -42,15 +44,15 @@ export const Playlists = ({
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
width: "100%",
|
||||
background: isCurrentVidPlayling && theme.palette.primary.main,
|
||||
background: isCurrentVidPlaying && theme.palette.primary.main,
|
||||
alignItems: "center",
|
||||
padding: "10px",
|
||||
borderRadius: "5px",
|
||||
cursor: isCurrentVidPlayling ? "default" : "pointer",
|
||||
cursor: isCurrentVidPlaying ? "default" : "pointer",
|
||||
userSelect: "none",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (isCurrentVidPlayling) return;
|
||||
if (isCurrentVidPlaying) return;
|
||||
onClick(vid.name, vid.identifier);
|
||||
// navigate(`/video/${vid.name}/${vid.identifier}`)
|
||||
}}
|
||||
|
@ -15,8 +15,10 @@ import {
|
||||
Typography,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { useSignal } from "@preact/signals-react";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import Compressor from "compressorjs";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useState } from "react";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
@ -27,6 +29,7 @@ import {
|
||||
} from "../../../constants/Identifiers.ts";
|
||||
import {
|
||||
maxSize,
|
||||
menuIconSize,
|
||||
titleFormatter,
|
||||
videoMaxSize,
|
||||
} from "../../../constants/Misc.ts";
|
||||
@ -38,7 +41,6 @@ import {
|
||||
|
||||
import { setNotification } from "../../../state/features/notificationsSlice.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { formatBytes } from "../../../utils/numberFunctions.ts";
|
||||
import { objectToBase64 } from "../../../utils/PublishFormatter.ts";
|
||||
import { getFileName } from "../../../utils/stringFunctions.ts";
|
||||
import { CardContentContainerComment } from "../../common/Comments/Comments-styles.tsx";
|
||||
@ -66,8 +68,7 @@ import {
|
||||
StyledButton,
|
||||
TimesIcon,
|
||||
} from "./PublishVideo-styles.tsx";
|
||||
import { signal, Signal, useSignal } from "@preact/signals-react";
|
||||
import { useSignals } from "@preact/signals-react/runtime";
|
||||
import VideoLibraryIcon from "@mui/icons-material/VideoLibrary";
|
||||
|
||||
export const toBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
|
||||
new Promise((resolve, reject) => {
|
||||
@ -82,13 +83,14 @@ export const toBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
|
||||
const uid = new ShortUniqueId();
|
||||
const shortuid = new ShortUniqueId({ length: 5 });
|
||||
|
||||
interface NewCrowdfundProps {
|
||||
interface PublishVideoProps {
|
||||
editId?: string;
|
||||
editContent?: null | {
|
||||
title: string;
|
||||
user: string;
|
||||
coverImage: string | null;
|
||||
};
|
||||
afterClose?: () => void;
|
||||
}
|
||||
|
||||
interface VideoFile {
|
||||
@ -97,7 +99,11 @@ interface VideoFile {
|
||||
description: string;
|
||||
coverImage?: string;
|
||||
}
|
||||
export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
|
||||
export const PublishVideo = ({
|
||||
editId,
|
||||
editContent,
|
||||
afterClose,
|
||||
}: PublishVideoProps) => {
|
||||
const theme = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
const [isOpenMultiplePublish, setIsOpenMultiplePublish] = useState(false);
|
||||
@ -188,13 +194,9 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
|
||||
},
|
||||
});
|
||||
|
||||
// useEffect(() => {
|
||||
// if (editContent) {
|
||||
// }
|
||||
// }, [editContent]);
|
||||
|
||||
const onClose = () => {
|
||||
setIsOpen(false);
|
||||
if (afterClose) afterClose();
|
||||
};
|
||||
|
||||
const search = async () => {
|
||||
@ -633,12 +635,20 @@ export const PublishVideo = ({ editId, editContent }: NewCrowdfundProps) => {
|
||||
{editId ? null : (
|
||||
<StyledButton
|
||||
color="primary"
|
||||
startIcon={<AddBoxIcon />}
|
||||
startIcon={
|
||||
<VideoLibraryIcon
|
||||
sx={{
|
||||
color: "#FF0033",
|
||||
width: menuIconSize,
|
||||
height: menuIconSize,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
onClick={() => {
|
||||
setIsOpen(true);
|
||||
}}
|
||||
>
|
||||
add video
|
||||
Video
|
||||
</StyledButton>
|
||||
)}
|
||||
</>
|
||||
|
@ -159,7 +159,7 @@ export const BlockIconContainer = styled(Box)({
|
||||
});
|
||||
|
||||
export const CommentsContainer = styled(Box)({
|
||||
width: "70%",
|
||||
width: "90%",
|
||||
maxWidth: "1000px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
|
@ -160,10 +160,10 @@ export const LikeAndDislike = ({ name, identifier }: LikeAndDislikeProps) => {
|
||||
sx={{
|
||||
padding: "5px",
|
||||
borderRadius: "7px",
|
||||
gap: "5px",
|
||||
gap: "10px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginRight: "10px",
|
||||
marginRight: "20px",
|
||||
height: "53px",
|
||||
}}
|
||||
>
|
||||
|
@ -1,32 +1,29 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import ThumbUpIcon from "@mui/icons-material/ThumbUp";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
FormControl,
|
||||
Input,
|
||||
InputAdornment,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
Modal,
|
||||
Select,
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
import qortImg from "../../../assets/img/qort.png";
|
||||
import { MultiplePublish } from "../../Publish/MultiplePublish/MultiplePublishAll.tsx";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { setNotification } from "../../../state/features/notificationsSlice.ts";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import qortImg from "../../../assets/img/qort.png";
|
||||
import {
|
||||
objectToBase64,
|
||||
objectToFile,
|
||||
} from "../../../utils/PublishFormatter.ts";
|
||||
FOR,
|
||||
FOR_SUPER_LIKE,
|
||||
SUPER_LIKE_BASE,
|
||||
} from "../../../constants/Identifiers.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 {
|
||||
CrowdfundActionButton,
|
||||
CrowdfundActionButtonRow,
|
||||
@ -34,16 +31,7 @@ import {
|
||||
NewCrowdfundTitle,
|
||||
Spacer,
|
||||
} from "../../Publish/PublishVideo/PublishVideo-styles.tsx";
|
||||
import { utf8ToBase64 } from "../SuperLikesList/CommentEditor.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";
|
||||
import { CommentInput } from "../Comments/Comments-styles.tsx";
|
||||
|
||||
const uid = new ShortUniqueId({ length: 4 });
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
@ -11,29 +11,28 @@ import {
|
||||
ListItemIcon,
|
||||
Popover,
|
||||
Typography,
|
||||
useTheme
|
||||
} from '@mui/material'
|
||||
import { Movie } from '@mui/icons-material'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { RootState } from '../../state/store'
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { DownloadingLight } from '../../assets/svgs/DownloadingLight'
|
||||
import { DownloadedLight } from '../../assets/svgs/DownloadedLight'
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { Movie } from "@mui/icons-material";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../state/store";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { DownloadingLight } from "../../assets/svgs/DownloadingLight";
|
||||
import { DownloadedLight } from "../../assets/svgs/DownloadedLight";
|
||||
|
||||
export const DownloadTaskManager: React.FC = () => {
|
||||
const { downloads } = useSelector((state: RootState) => state.global)
|
||||
const location = useLocation()
|
||||
const theme = useTheme()
|
||||
const [visible, setVisible] = useState(false)
|
||||
const [hidden, setHidden] = useState(true)
|
||||
const navigate = useNavigate()
|
||||
const { downloads } = useSelector((state: RootState) => state.global);
|
||||
const theme = useTheme();
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [hidden, setHidden] = useState(true);
|
||||
const navigate = useNavigate();
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||
|
||||
|
||||
const [openDownload, setOpenDownload] = useState<boolean>(false);
|
||||
|
||||
|
||||
const hashMapVideos = useSelector(
|
||||
(state: RootState) => state.video.hashMapVideos
|
||||
);
|
||||
const handleClick = (event?: React.MouseEvent<HTMLDivElement>) => {
|
||||
const target = event?.currentTarget as unknown as HTMLButtonElement | null;
|
||||
setAnchorEl(target);
|
||||
@ -49,155 +48,159 @@ export const DownloadTaskManager: React.FC = () => {
|
||||
|
||||
if (visible) {
|
||||
setTimeout(() => {
|
||||
setHidden(true)
|
||||
setVisible(false)
|
||||
}, 3000)
|
||||
setHidden(true);
|
||||
setVisible(false);
|
||||
}, 3000);
|
||||
}
|
||||
}, [visible])
|
||||
|
||||
}, [visible]);
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.keys(downloads).length === 0) return
|
||||
setVisible(true)
|
||||
setHidden(false)
|
||||
}, [downloads])
|
||||
if (Object.keys(downloads).length === 0) return;
|
||||
setVisible(true);
|
||||
setHidden(false);
|
||||
}, [downloads]);
|
||||
|
||||
if (!downloads || Object.keys(downloads).length === 0) return null;
|
||||
|
||||
let downloadInProgress = false;
|
||||
if (
|
||||
!downloads ||
|
||||
Object.keys(downloads).length === 0
|
||||
)
|
||||
return null
|
||||
|
||||
|
||||
let downloadInProgress = false
|
||||
if(Object.keys(downloads).find((key)=> (downloads[key]?.status?.status !== 'READY' && downloads[key]?.status?.status !== 'DOWNLOADED'))){
|
||||
downloadInProgress = true
|
||||
Object.keys(downloads).find(
|
||||
key =>
|
||||
downloads[key]?.status?.status !== "READY" &&
|
||||
downloads[key]?.status?.status !== "DOWNLOADED"
|
||||
)
|
||||
) {
|
||||
downloadInProgress = true;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Button onClick={(e: any) => {
|
||||
handleClick(e);
|
||||
setOpenDownload(true);
|
||||
}}>
|
||||
{downloadInProgress ? (
|
||||
<DownloadingLight height='24px' width='24px' className='download-icon' />
|
||||
) : (
|
||||
<DownloadedLight height='24px' width='24px' />
|
||||
)}
|
||||
<Button
|
||||
sx={{ padding: "0px 0px", minWidth: "0px" }}
|
||||
onClick={(e: any) => {
|
||||
handleClick(e);
|
||||
setOpenDownload(true);
|
||||
}}
|
||||
>
|
||||
{downloadInProgress ? (
|
||||
<DownloadingLight
|
||||
height="24px"
|
||||
width="24px"
|
||||
className="download-icon"
|
||||
/>
|
||||
) : (
|
||||
<DownloadedLight height="24px" width="24px" />
|
||||
)}
|
||||
</Button>
|
||||
|
||||
</Button>
|
||||
|
||||
<Popover
|
||||
id={"download-popover"}
|
||||
open={openDownload}
|
||||
anchorEl={anchorEl}
|
||||
onClose={handleCloseDownload}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left"
|
||||
<Popover
|
||||
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
|
||||
sx={{
|
||||
maxHeight: '50vh',
|
||||
overflow: 'auto',
|
||||
width: '250px',
|
||||
gap: '5px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
{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;
|
||||
const id =
|
||||
downloadObj?.identifier + "_metadata-" + downloadObj?.name;
|
||||
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}
|
||||
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 userName = downloadObj?.name;
|
||||
const identifier = downloadObj?.identifier;
|
||||
|
||||
if (identifier && userName)
|
||||
navigate(`/video/${userName}/${identifier}_metadata`);
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
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={{
|
||||
borderRadius: "5px",
|
||||
color: theme.palette.secondary.main,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Typography
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
background: theme.palette.primary.main,
|
||||
fontFamily: "Arial",
|
||||
color: theme.palette.text.primary,
|
||||
cursor: 'pointer',
|
||||
padding: '2px',
|
||||
|
||||
}}
|
||||
onClick={() => {
|
||||
const id = downloadObj?.properties?.jsonId
|
||||
if (!id) return
|
||||
|
||||
navigate(
|
||||
`/video/${downloadObj?.properties?.user}/${id}`
|
||||
)
|
||||
}}
|
||||
variant="caption"
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
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={{
|
||||
borderRadius: '5px',
|
||||
color: theme.palette.secondary.main
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: 'Arial',
|
||||
color: theme.palette.text.primary
|
||||
}}
|
||||
variant="caption"
|
||||
>
|
||||
{`${progress?.toFixed(0)}%`}{' '}
|
||||
{status && status === 'REFETCHING' && '- refetching'}
|
||||
{status && status === 'DOWNLOADED' && '- building'}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '10px',
|
||||
width: '100%',
|
||||
textAlign: 'end',
|
||||
fontFamily: 'Arial',
|
||||
color: theme.palette.text.primary,
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{downloadObj?.identifier}
|
||||
</Typography>
|
||||
</ListItem>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
</Popover>
|
||||
|
||||
{`${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>
|
||||
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -1,16 +1,18 @@
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { Box, Tooltip, Typography, useMediaQuery } from "@mui/material";
|
||||
import React from "react";
|
||||
import { PopMenu } from "../PopMenu.tsx";
|
||||
import ListSuperLikes from "./ListSuperLikes";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../../state/store";
|
||||
|
||||
import ThumbUpIcon from "@mui/icons-material/ThumbUp";
|
||||
export const ListSuperLikeContainer = () => {
|
||||
const superlikelist = useSelector(
|
||||
(state: RootState) => state.global.superlikelistAll
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
const isScreenLarge = useMediaQuery("(min-width:1200px)");
|
||||
const superlikeListComponent = (
|
||||
<>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
@ -20,6 +22,53 @@ export const ListSuperLikeContainer = () => {
|
||||
Recent Super likes
|
||||
</Typography>
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
@ -64,7 +64,6 @@ export function extractIdValue(metadescription) {
|
||||
}
|
||||
|
||||
export const Notifications = () => {
|
||||
const dispatch = useDispatch();
|
||||
const [anchorElNotification, setAnchorElNotification] =
|
||||
useState<HTMLButtonElement | null>(null);
|
||||
const [notifications, setNotifications] = useState<any[]>([]);
|
||||
@ -238,7 +237,7 @@ export const Notifications = () => {
|
||||
badgeContent={notificationBadgeLength}
|
||||
color="primary"
|
||||
sx={{
|
||||
margin: "0px 12px",
|
||||
margin: "0px",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
@ -267,6 +266,7 @@ export const Notifications = () => {
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
}}
|
||||
sx={{ marginTop: "12px" }}
|
||||
>
|
||||
<Box>
|
||||
<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,
|
||||
DialogTitle,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import React, { useCallback, useState, useEffect } from "react";
|
||||
import ThumbUpIcon from "@mui/icons-material/ThumbUp";
|
||||
import {
|
||||
fontSizeSmall,
|
||||
smallScreenSizeString,
|
||||
} from "../../../constants/Misc.ts";
|
||||
|
||||
import { CommentEditor } from "./CommentEditor";
|
||||
import {
|
||||
@ -36,9 +41,9 @@ interface CommentProps {
|
||||
postId: string;
|
||||
postName: string;
|
||||
onSubmit: (obj?: any, isEdit?: boolean) => void;
|
||||
amount?: null | number
|
||||
isSuperLike?: boolean
|
||||
hasHash?: boolean
|
||||
amount?: null | number;
|
||||
isSuperLike?: boolean;
|
||||
hasHash?: boolean;
|
||||
}
|
||||
export const Comment = ({
|
||||
comment,
|
||||
@ -47,7 +52,7 @@ export const Comment = ({
|
||||
onSubmit,
|
||||
amount,
|
||||
isSuperLike,
|
||||
hasHash
|
||||
hasHash,
|
||||
}: CommentProps) => {
|
||||
const [isReplying, setIsReplying] = useState<boolean>(false);
|
||||
const [isEditing, setIsEditing] = useState<boolean>(false);
|
||||
@ -199,7 +204,7 @@ export const CommentCard = ({
|
||||
children,
|
||||
setCurrentEdit,
|
||||
isReply,
|
||||
amount
|
||||
amount,
|
||||
}: any) => {
|
||||
const [avatarUrl, setAvatarUrl] = React.useState<string>("");
|
||||
const { user } = useSelector((state: RootState) => state.auth);
|
||||
@ -225,6 +230,13 @@ export const CommentCard = ({
|
||||
getAvatar(name);
|
||||
}, [name]);
|
||||
|
||||
const isScreenSmall = !useMediaQuery(`(min-width:${smallScreenSizeString})`);
|
||||
const superLikeHeaderSX = {
|
||||
display: "flex",
|
||||
flexDirection: isScreenSmall ? "column" : "row",
|
||||
alignItems: "center",
|
||||
};
|
||||
|
||||
return (
|
||||
<CardContentContainerComment>
|
||||
<StyledCardHeaderComment
|
||||
@ -234,40 +246,48 @@ export const CommentCard = ({
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Avatar
|
||||
src={avatarUrl}
|
||||
alt={`${name}'s avatar`}
|
||||
sx={{ width: "35px", height: "35px" }}
|
||||
/>
|
||||
</Box>
|
||||
<StyledCardColComment>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
<AuthorTextComment>{name}</AuthorTextComment>
|
||||
{!isReply && (
|
||||
<ThumbUpIcon
|
||||
style={{
|
||||
color: "gold",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{amount && (
|
||||
<Typography sx={{
|
||||
fontSize: '20px',
|
||||
color: 'gold'
|
||||
}}>
|
||||
{parseFloat(amount)?.toFixed(2)} QORT
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<Box sx={superLikeHeaderSX}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
src={avatarUrl}
|
||||
alt={`${name}'s avatar`}
|
||||
sx={{ width: "35px", height: "35px" }}
|
||||
/>
|
||||
<AuthorTextComment>{name}</AuthorTextComment>
|
||||
</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>
|
||||
<StyledCardContentComment>
|
||||
<StyledCardComment>{message}</StyledCardComment>
|
||||
|
@ -63,9 +63,9 @@ export const StyledCardCol = styled(Box)({
|
||||
export const StyledCardColComment = styled(Box)({
|
||||
display: "flex",
|
||||
overflow: "hidden",
|
||||
flexDirection: "column",
|
||||
flexDirection: "row",
|
||||
gap: "2px",
|
||||
alignItems: "flex-start",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
});
|
||||
|
||||
@ -159,7 +159,7 @@ export const BlockIconContainer = styled(Box)({
|
||||
});
|
||||
|
||||
export const CommentsContainer = styled(Box)({
|
||||
width: "70%",
|
||||
width: "98%",
|
||||
maxWidth: "1000px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
|
@ -42,7 +42,6 @@ export const MobileControls = () => {
|
||||
<IconButton
|
||||
sx={{
|
||||
color: "rgba(255, 255, 255, 0.7)",
|
||||
marginLeft: "15px",
|
||||
}}
|
||||
onClick={reloadVideo}
|
||||
>
|
||||
@ -55,11 +54,26 @@ export const MobileControls = () => {
|
||||
max={videoRef.current?.duration || 100}
|
||||
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
|
||||
edge="end"
|
||||
color="inherit"
|
||||
aria-label="menu"
|
||||
onClick={handleMenuOpen}
|
||||
sx={{ minWidth: "30px" }}
|
||||
>
|
||||
<MoreIcon />
|
||||
</IconButton>
|
||||
@ -85,22 +99,10 @@ export const MobileControls = () => {
|
||||
step={0.01}
|
||||
/>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => increaseSpeed()}>
|
||||
<Typography
|
||||
sx={{
|
||||
color: "rgba(255, 255, 255, 0.7)",
|
||||
fontSize: "14px",
|
||||
}}
|
||||
>
|
||||
Speed: {playbackRate}x
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem onClick={togglePictureInPicture}>
|
||||
<PictureInPicture />
|
||||
</MenuItem>
|
||||
<MenuItem onClick={toggleFullscreen}>
|
||||
<Fullscreen />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
|
@ -3,11 +3,13 @@ import { useSignalEffect, useSignals } from "@preact/signals-react/runtime";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { Key } from "ts-key-enum";
|
||||
import { useIsMobile } from "../../../../hooks/useIsMobile.ts";
|
||||
import { setVideoPlaying } from "../../../../state/features/globalSlice.ts";
|
||||
import {
|
||||
setIsMuted,
|
||||
setMutedVolumeSetting,
|
||||
setReduxPlaybackRate,
|
||||
setStretchVideoSetting,
|
||||
setVolumeSetting,
|
||||
@ -36,13 +38,12 @@ export const useVideoControlsState = (
|
||||
progress,
|
||||
videoObjectFit,
|
||||
canPlay,
|
||||
isMobileView,
|
||||
} = videoPlayerState;
|
||||
const { identifier, autoPlay } = props;
|
||||
|
||||
const isMobile = useIsMobile();
|
||||
const showControlsFullScreen = useSignal(true);
|
||||
const persistSelector = store.getState().persist;
|
||||
const dispatch = useDispatch();
|
||||
const persistSelector = useSelector((root: RootState) => root.persist);
|
||||
|
||||
const videoPlaying = useSelector(
|
||||
(state: RootState) => state.global.videoPlaying
|
||||
@ -58,7 +59,6 @@ export const useVideoControlsState = (
|
||||
|
||||
videoRef.current.playbackRate = newSpeed;
|
||||
playbackRate.value = newSpeed;
|
||||
store.dispatch(setReduxPlaybackRate(newSpeed));
|
||||
}
|
||||
};
|
||||
|
||||
@ -156,10 +156,9 @@ export const useVideoControlsState = (
|
||||
const onVolumeChange = (_: any, value: number | number[]) => {
|
||||
if (!videoRef.current) return;
|
||||
const newVolume = value as number;
|
||||
videoRef.current.volume = newVolume;
|
||||
volume.value = newVolume;
|
||||
isMuted.value = false;
|
||||
store.dispatch(setVolumeSetting(newVolume));
|
||||
mutedVolume.value = newVolume;
|
||||
volume.value = newVolume;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@ -170,12 +169,10 @@ export const useVideoControlsState = (
|
||||
isMuted.value = true;
|
||||
mutedVolume.value = volume.value;
|
||||
volume.value = 0;
|
||||
if (videoRef.current) videoRef.current.volume = 0;
|
||||
};
|
||||
const unMute = () => {
|
||||
isMuted.value = false;
|
||||
volume.value = mutedVolume.value;
|
||||
if (videoRef.current) videoRef.current.volume = mutedVolume.value;
|
||||
};
|
||||
|
||||
const toggleMute = () => {
|
||||
@ -191,12 +188,10 @@ export const useVideoControlsState = (
|
||||
|
||||
newVolume = Math.max(newVolume, minVolume);
|
||||
newVolume = Math.min(newVolume, maxVolume);
|
||||
|
||||
newVolume = +newVolume.toFixed(2);
|
||||
isMuted.value = false;
|
||||
mutedVolume.value = newVolume;
|
||||
videoRef.current.volume = newVolume;
|
||||
volume.value = newVolume;
|
||||
store.dispatch(setVolumeSetting(newVolume));
|
||||
}
|
||||
};
|
||||
const setProgressRelative = (secondsChange: number) => {
|
||||
@ -228,7 +223,6 @@ export const useVideoControlsState = (
|
||||
persistSelector.stretchVideoSetting === "contain" ? "fill" : "contain";
|
||||
|
||||
videoObjectFit.value = newStretchVideoSetting;
|
||||
store.dispatch(setStretchVideoSetting(newStretchVideoSetting));
|
||||
};
|
||||
const keyboardShortcutsDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
e.preventDefault();
|
||||
@ -350,14 +344,13 @@ export const useVideoControlsState = (
|
||||
videoRef.current.play().then(() => {
|
||||
playing.value = true;
|
||||
startPlay.value = true;
|
||||
store.dispatch(setVideoPlaying(null));
|
||||
dispatch(setVideoPlaying(null));
|
||||
});
|
||||
}
|
||||
}, [videoPlaying, identifier, src]);
|
||||
|
||||
useSignalEffect(() => {
|
||||
console.log("canPlay is: ", canPlay.value); // makes the function execute when canPlay changes
|
||||
if (isMobile) isMobileView.value = true;
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -7,12 +7,14 @@ import {
|
||||
VolumeOff,
|
||||
VolumeUp,
|
||||
} 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 { ControlsContainer } from "../VideoPlayer-styles.ts";
|
||||
import { MobileControls } from "./MobileControls.tsx";
|
||||
import { useVideoContext } from "./VideoContext.ts";
|
||||
import { useSignalEffect } from "@preact/signals-react";
|
||||
|
||||
export const VideoControls = () => {
|
||||
const {
|
||||
@ -28,7 +30,6 @@ export const VideoControls = () => {
|
||||
from,
|
||||
videoRef,
|
||||
canPlay,
|
||||
isMobileView,
|
||||
isMuted,
|
||||
playbackRate,
|
||||
playing,
|
||||
@ -37,8 +38,8 @@ export const VideoControls = () => {
|
||||
showControlsFullScreen,
|
||||
} = useVideoContext();
|
||||
|
||||
const showMobileControls =
|
||||
isMobileView.value && canPlay.value && showControlsFullScreen.value;
|
||||
const isScreenSmall = !useMediaQuery(`(min-width:580px)`);
|
||||
const showMobileControls = isScreenSmall && canPlay.value;
|
||||
|
||||
return (
|
||||
<ControlsContainer
|
||||
|
@ -1,4 +1,8 @@
|
||||
import { useSignal, useSignals } from "@preact/signals-react/runtime";
|
||||
import {
|
||||
useSignal,
|
||||
useSignalEffect,
|
||||
useSignals,
|
||||
} from "@preact/signals-react/runtime";
|
||||
import React, {
|
||||
useContext,
|
||||
useEffect,
|
||||
@ -9,7 +13,14 @@ import React, {
|
||||
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
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 { MyContext } from "../../../wrappers/DownloadWrapper.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 playing = useSignal(false);
|
||||
const isMuted = useSignal(false);
|
||||
const progress = useSignal(0);
|
||||
const isLoading = useSignal(false);
|
||||
const canPlay = useSignal(false);
|
||||
const startPlay = useSignal(false);
|
||||
const isMobileView = useSignal(false);
|
||||
|
||||
const isMuted = useSignal(persistSelector.isMuted);
|
||||
const volume = useSignal(persistSelector.volume);
|
||||
const mutedVolume = useSignal(persistSelector.volume);
|
||||
const mutedVolume = useSignal(persistSelector.mutedVolume);
|
||||
const playbackRate = useSignal(persistSelector.playbackRate);
|
||||
const anchorEl = useSignal(null);
|
||||
const videoObjectFit = useSignal<StretchVideoType>(
|
||||
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 {
|
||||
name,
|
||||
identifier,
|
||||
@ -279,7 +310,6 @@ export const useVideoPlayerState = (props: VideoPlayerProps, ref: any) => {
|
||||
isLoading,
|
||||
canPlay,
|
||||
startPlay,
|
||||
isMobileView,
|
||||
volume,
|
||||
mutedVolume,
|
||||
playbackRate,
|
||||
|
@ -55,7 +55,6 @@ export const VideoPlayer = forwardRef<videoRefType, VideoPlayerProps>(
|
||||
startPlay,
|
||||
videoObjectFit,
|
||||
showControlsFullScreen,
|
||||
duration,
|
||||
} = contextData;
|
||||
|
||||
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 { LightModeSVG } from "../../../assets/svgs/LightModeSVG";
|
||||
import { DarkModeSVG } from "../../../assets/svgs/DarkModeSVG";
|
||||
import { fontSizeSmall } from "../../../constants/Misc.ts";
|
||||
|
||||
export const CustomAppBar = styled(AppBar)(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
justifyContent: "start",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
padding: "5px 16px",
|
||||
padding: "5px 16px 5px 5px",
|
||||
backgroundImage: "none",
|
||||
borderBottom: `1px solid ${theme.palette.primary.light}`,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
[theme.breakpoints.only("xs")]: {
|
||||
gap: "15px",
|
||||
},
|
||||
height: "50px",
|
||||
}));
|
||||
export const LogoContainer = styled("div")({
|
||||
@ -85,16 +83,15 @@ export const DropdownText = styled(Typography)(({ theme }) => ({
|
||||
|
||||
export const NavbarName = styled(Typography)(({ theme }) => ({
|
||||
fontFamily: "Raleway",
|
||||
fontSize: "18px",
|
||||
fontSize: fontSizeSmall,
|
||||
color: theme.palette.text.primary,
|
||||
margin: "0 10px",
|
||||
marginRight: "10px",
|
||||
}));
|
||||
|
||||
export const ThemeSelectRow = styled(Box)({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
flexBasis: 0,
|
||||
gap: "10px",
|
||||
height: "100%",
|
||||
});
|
||||
|
||||
|
@ -1,43 +1,12 @@
|
||||
import React, { useState, useRef } from "react";
|
||||
import { Box, Button, Input, Popover, useTheme } from "@mui/material";
|
||||
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 { Box, useMediaQuery } from "@mui/material";
|
||||
import React from "react";
|
||||
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 { PublishMenu } from "./Components/PublishMenu.tsx";
|
||||
import { QtubeLogo } from "./Components/QtubeLogo.tsx";
|
||||
import { UserMenu } from "./Components/UserMenu.tsx";
|
||||
import { CustomAppBar } from "./Navbar-styles";
|
||||
|
||||
interface Props {
|
||||
isAuthenticated: boolean;
|
||||
userName: string | null;
|
||||
@ -46,296 +15,39 @@ interface Props {
|
||||
setTheme: (val: string) => void;
|
||||
}
|
||||
|
||||
const NavBar: React.FC<Props> = ({
|
||||
isAuthenticated,
|
||||
userName,
|
||||
userAvatar,
|
||||
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);
|
||||
};
|
||||
const NavBar: React.FC<Props> = ({ isAuthenticated, userName, userAvatar }) => {
|
||||
const isScreenSmall = !useMediaQuery(`(min-width:600px)`);
|
||||
const isSecure = isAuthenticated && !!userName;
|
||||
const gapSize = 10;
|
||||
|
||||
return (
|
||||
<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
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
gap: `${gapSize}px`,
|
||||
}}
|
||||
>
|
||||
<Popover
|
||||
id={idNotification}
|
||||
open={openPopover}
|
||||
anchorEl={anchorElNotification}
|
||||
onClose={closeNotificationPopover}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "left",
|
||||
<QtubeLogo />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: `${isScreenSmall ? gapSize : gapSize * 2}px`,
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
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",
|
||||
}}
|
||||
/>
|
||||
{isSecure && <Notifications />}
|
||||
|
||||
<SearchIcon
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
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}
|
||||
<DownloadTaskManager />
|
||||
<UserMenu
|
||||
isShowMenu={isSecure}
|
||||
userAvatar={userAvatar}
|
||||
userName={userName}
|
||||
/>
|
||||
)}
|
||||
<PublishMenu isDisplayed={isSecure} />
|
||||
</Box>
|
||||
</Box>
|
||||
</CustomAppBar>
|
||||
);
|
||||
|
@ -14,3 +14,12 @@ export const fontSizeExLarge = "150%";
|
||||
export const maxCommentLength = 10_000;
|
||||
export const minFileSize = 1_000;
|
||||
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 { CommentSection } from "../../../components/common/Comments/CommentSection.tsx";
|
||||
import { SuperLikesSection } from "../../../components/common/SuperLikesList/SuperLikesSection.tsx";
|
||||
import { DisplayHtml } from "../../../components/common/TextEditor/DisplayHtml.tsx";
|
||||
import { VideoPlayer } from "../../../components/common/VideoPlayer/VideoPlayer.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 { VideoActionsBar } from "../VideoContent/VideoActionsBar.tsx";
|
||||
import { usePlaylistContentState } from "./PlaylistContent-State.ts";
|
||||
@ -40,6 +46,8 @@ export const PlaylistContent = () => {
|
||||
loadingSuperLikes,
|
||||
} = usePlaylistContentState();
|
||||
|
||||
const isScreenSmall = !useMediaQuery(`(min-width:700px)`);
|
||||
|
||||
return videoData && videoData?.videos?.length === 0 ? (
|
||||
<Box
|
||||
sx={{
|
||||
@ -55,197 +63,202 @@ export const PlaylistContent = () => {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
padding: "0px 10px",
|
||||
marginLeft: "5%",
|
||||
padding: "0px",
|
||||
marginLeft: "2%",
|
||||
}}
|
||||
onClick={focusVideo}
|
||||
>
|
||||
<VideoPlayerContainer
|
||||
sx={{
|
||||
marginBottom: "30px",
|
||||
width: "100%",
|
||||
display: "grid",
|
||||
gridTemplateColumns: isScreenSmall ? "1fr" : "60vw auto",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<>
|
||||
{videoReference && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "55vw 35vw",
|
||||
width: "100vw",
|
||||
gap: "3vw",
|
||||
aspectRatio: "16/9",
|
||||
}}
|
||||
>
|
||||
{videoReference && (
|
||||
<Box
|
||||
sx={{
|
||||
aspectRatio: "16/9",
|
||||
}}
|
||||
>
|
||||
<VideoPlayer
|
||||
name={videoReference?.name}
|
||||
service={videoReference?.service}
|
||||
identifier={videoReference?.identifier}
|
||||
user={channelName}
|
||||
jsonId={id}
|
||||
poster={videoCover || ""}
|
||||
nextVideo={nextVideo}
|
||||
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}
|
||||
/>
|
||||
)}
|
||||
<VideoPlayer
|
||||
name={videoReference?.name}
|
||||
service={videoReference?.service}
|
||||
identifier={videoReference?.identifier}
|
||||
user={channelName}
|
||||
jsonId={id}
|
||||
poster={videoCover || ""}
|
||||
nextVideo={nextVideo}
|
||||
onEnd={onEndVideo}
|
||||
autoPlay={doAutoPlay}
|
||||
ref={containerRef}
|
||||
videoStyles={{
|
||||
videoContainer: { aspectRatio: "16 / 9" },
|
||||
video: { aspectRatio: "16 / 9" },
|
||||
}}
|
||||
/>
|
||||
</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 && (
|
||||
<CommentSection
|
||||
postId={videoData?.id || ""}
|
||||
postName={channelName || ""}
|
||||
{playlistData && (
|
||||
<Playlists
|
||||
playlistData={playlistData}
|
||||
currentVideoIdentifier={videoData?.id}
|
||||
onClick={getVideoData}
|
||||
/>
|
||||
)}
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
@ -1,75 +1,22 @@
|
||||
import { Avatar, Box, useTheme } from "@mui/material";
|
||||
import { FollowButton } from "../../../components/common/ContentButtons/FollowButton.tsx";
|
||||
import { SubscribeButton } from "../../../components/common/ContentButtons/SubscribeButton.tsx";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import {
|
||||
AuthorTextComment,
|
||||
StyledCardColComment,
|
||||
StyledCardHeaderComment,
|
||||
} from "./VideoContent-styles.tsx";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Box, SxProps, Theme, useMediaQuery } from "@mui/material";
|
||||
import { smallScreenSizeString } from "../../../constants/Misc.ts";
|
||||
import { ChannelButtons } from "./ChannelButtons.tsx";
|
||||
import { ChannelName, ChannelParams } from "./ChannelName.tsx";
|
||||
import { Spacer } from "./VideoContent-styles.tsx";
|
||||
|
||||
export interface ChannelActionsParams {
|
||||
channelName: string;
|
||||
}
|
||||
export const ChannelActions = ({ channelName }: ChannelActionsParams) => {
|
||||
const navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
const userName = useSelector((state: RootState) => state.auth.user?.name);
|
||||
export const ChannelActions = ({ channelName, sx }: ChannelParams) => {
|
||||
const boxSX: SxProps<Theme> = {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
flexWrap: "wrap",
|
||||
rowGap: "10px",
|
||||
columnGap: "20px",
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<StyledCardHeaderComment
|
||||
sx={{
|
||||
"& .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 sx={{ ...boxSX, ...sx }}>
|
||||
<ChannelName channelName={channelName} />
|
||||
<ChannelButtons channelName={channelName} />
|
||||
</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 { FollowButton } from "../../../components/common/ContentButtons/FollowButton.tsx";
|
||||
import DownloadIcon from "@mui/icons-material/Download";
|
||||
import { Box, SxProps, Theme, useMediaQuery } from "@mui/material";
|
||||
import { useMemo } from "react";
|
||||
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 FileElement from "../../../components/common/FileElement.tsx";
|
||||
import { videoRefType } from "../../../components/common/VideoPlayer/VideoPlayer.tsx";
|
||||
import { titleFormatterOnSave } from "../../../constants/Misc.ts";
|
||||
import { useFetchSuperLikes } from "../../../hooks/useFetchSuperLikes.tsx";
|
||||
import DownloadIcon from "@mui/icons-material/Download";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import {
|
||||
smallScreenSizeString,
|
||||
titleFormatterOnSave,
|
||||
} from "../../../constants/Misc.ts";
|
||||
import { ChannelActions } from "./ChannelActions.tsx";
|
||||
import {
|
||||
AuthorTextComment,
|
||||
FileAttachmentContainer,
|
||||
FileAttachmentFont,
|
||||
StyledCardColComment,
|
||||
StyledCardHeaderComment,
|
||||
} from "./VideoContent-styles.tsx";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
export interface VideoActionsBarProps {
|
||||
channelName: string;
|
||||
@ -82,11 +75,12 @@ export const VideoActionsBar = ({
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "80%",
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr 1fr",
|
||||
marginTop: "15px",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexWrap: "wrap",
|
||||
gap: "20px",
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
@ -95,6 +89,8 @@ export const VideoActionsBar = ({
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
{videoData && (
|
||||
@ -110,28 +106,30 @@ export const VideoActionsBar = ({
|
||||
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>
|
||||
|
||||
{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>
|
||||
);
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { styled } from "@mui/system";
|
||||
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 }) => ({
|
||||
display: "flex",
|
||||
@ -8,10 +9,7 @@ export const VideoContentContainer = styled(Box)(({ theme }) => ({
|
||||
alignItems: "start",
|
||||
}));
|
||||
|
||||
export const VideoPlayerContainer = styled(Box)(({ theme }) => ({
|
||||
width: "55vw",
|
||||
marginLeft: "5%",
|
||||
}));
|
||||
export const VideoPlayerContainer = styled(Box)(({ theme }) => ({}));
|
||||
|
||||
export const VideoTitle = styled(Typography)(({ theme }) => ({
|
||||
fontFamily: "Raleway",
|
||||
@ -57,14 +55,14 @@ export const StyledCardCol = styled(Box)({
|
||||
export const StyledCardColComment = styled(Box)({
|
||||
display: "flex",
|
||||
overflow: "hidden",
|
||||
flexDirection: "column",
|
||||
flexDirection: "row",
|
||||
gap: "2px",
|
||||
alignItems: "flex-start",
|
||||
});
|
||||
|
||||
export const AuthorTextComment = styled(Typography)({
|
||||
fontFamily: "Raleway, sans-serif",
|
||||
fontSize: "20px",
|
||||
fontSize: fontSizeMedium,
|
||||
lineHeight: "1.2",
|
||||
});
|
||||
|
||||
@ -73,7 +71,6 @@ export const FileAttachmentContainer = styled(Box)(({ theme }) => ({
|
||||
alignItems: "center",
|
||||
padding: "5px 10px",
|
||||
border: `1px solid ${theme.palette.text.primary}`,
|
||||
width: "350px",
|
||||
height: "50px",
|
||||
}));
|
||||
|
||||
|
@ -1,12 +1,18 @@
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import React from "react";
|
||||
import { Box, Typography, useMediaQuery } from "@mui/material";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import DeletedVideo from "../../../assets/img/DeletedVideo.jpg";
|
||||
import { CommentSection } from "../../../components/common/Comments/CommentSection.tsx";
|
||||
import { SuperLikesSection } from "../../../components/common/SuperLikesList/SuperLikesSection.tsx";
|
||||
import { DisplayHtml } from "../../../components/common/TextEditor/DisplayHtml.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 { formatDate } from "../../../utils/time.ts";
|
||||
import { VideoActionsBar } from "./VideoActionsBar.tsx";
|
||||
@ -18,6 +24,7 @@ import {
|
||||
VideoPlayerContainer,
|
||||
VideoTitle,
|
||||
} from "./VideoContent-styles.tsx";
|
||||
import { useSignal } from "@preact/signals-react";
|
||||
|
||||
export const VideoContent = () => {
|
||||
const {
|
||||
@ -39,18 +46,47 @@ export const VideoContent = () => {
|
||||
superLikeList,
|
||||
setSuperLikeList,
|
||||
} = 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 (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
padding: "0px 10px 0px 5%",
|
||||
padding: `0px 0px 0px ${isScreenSmall ? "5px" : "2%"}`,
|
||||
width: "100%",
|
||||
}}
|
||||
onClick={focusVideo}
|
||||
>
|
||||
{videoReference ? (
|
||||
<VideoPlayerContainer>
|
||||
<VideoPlayerContainer
|
||||
sx={{
|
||||
width: `${videoWidth}%`,
|
||||
marginLeft: "0%",
|
||||
}}
|
||||
>
|
||||
<VideoPlayer
|
||||
name={videoReference?.name}
|
||||
service={videoReference?.service}
|
||||
@ -77,64 +113,61 @@ export const VideoContent = () => {
|
||||
<Box sx={{ width: "55vw", aspectRatio: "16/9" }}></Box>
|
||||
)}
|
||||
<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
|
||||
channelName={channelName}
|
||||
videoData={videoData}
|
||||
setSuperLikeList={setSuperLikeList}
|
||||
superLikeList={superLikeList}
|
||||
videoReference={videoReference}
|
||||
sx={{ width: "calc(100% - 5px)" }}
|
||||
/>
|
||||
|
||||
<Box
|
||||
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" />
|
||||
<Spacer height="15px" />
|
||||
{videoData?.fullDescription && (
|
||||
<Box
|
||||
sx={{
|
||||
background: "#333333",
|
||||
borderRadius: "5px",
|
||||
padding: "5px",
|
||||
width: "70%",
|
||||
width: "95%",
|
||||
cursor: !descriptionHeight
|
||||
? "default"
|
||||
: isExpandedDescription
|
||||
|
@ -7,9 +7,11 @@ import {
|
||||
MenuItem,
|
||||
OutlinedInput,
|
||||
Select,
|
||||
useMediaQuery,
|
||||
} from "@mui/material";
|
||||
import { StatsData } from "../../../components/StatsData.tsx";
|
||||
import { categories, subCategories } from "../../../constants/Categories.ts";
|
||||
import { smallScreenSizeString } from "../../../constants/Misc.ts";
|
||||
import { useSidebarState } from "./SearchSidebar-State.ts";
|
||||
import {
|
||||
FiltersCol,
|
||||
@ -38,8 +40,19 @@ export const SearchSidebar = ({ onSearch }: SearchSidebarProps) => {
|
||||
filtersToDefault,
|
||||
} = useSidebarState(onSearch);
|
||||
|
||||
const filtersStyle = { width: "75px", marginRight: "10px" };
|
||||
const isScreenSmall = !useMediaQuery(`(min-width:600px)`);
|
||||
|
||||
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>
|
||||
<StatsData />
|
||||
<Input
|
||||
@ -101,12 +114,11 @@ export const SearchSidebar = ({ onSearch }: SearchSidebarProps) => {
|
||||
/>
|
||||
|
||||
<FiltersSubContainer>
|
||||
<FormControl sx={{ width: "100%", marginTop: "30px" }}>
|
||||
<FormControl sx={{ width: "98%", marginTop: "30px" }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
@ -201,7 +213,7 @@ export const SearchSidebar = ({ onSearch }: SearchSidebarProps) => {
|
||||
</FiltersSubContainer>
|
||||
<FiltersSubContainer>
|
||||
<FiltersRow>
|
||||
Videos
|
||||
<span style={filtersStyle}>Videos</span>
|
||||
<FiltersRadioButton
|
||||
checked={filterType === "videos"}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@ -211,7 +223,7 @@ export const SearchSidebar = ({ onSearch }: SearchSidebarProps) => {
|
||||
/>
|
||||
</FiltersRow>
|
||||
<FiltersRow>
|
||||
Playlists
|
||||
<span style={filtersStyle}> Playlists</span>
|
||||
<FiltersRadioButton
|
||||
checked={filterType === "playlists"}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@ -221,12 +233,15 @@ export const SearchSidebar = ({ onSearch }: SearchSidebarProps) => {
|
||||
/>
|
||||
</FiltersRow>
|
||||
</FiltersSubContainer>
|
||||
|
||||
<Button
|
||||
onClick={() => {
|
||||
filtersToDefault();
|
||||
}}
|
||||
sx={{
|
||||
marginTop: "20px",
|
||||
width: "80%",
|
||||
alignSelf: "center",
|
||||
}}
|
||||
variant="contained"
|
||||
>
|
||||
@ -238,12 +253,14 @@ export const SearchSidebar = ({ onSearch }: SearchSidebarProps) => {
|
||||
}}
|
||||
sx={{
|
||||
marginTop: "20px",
|
||||
width: "80%",
|
||||
alignSelf: "center",
|
||||
}}
|
||||
variant="contained"
|
||||
>
|
||||
Search
|
||||
</Button>
|
||||
</FiltersContainer>
|
||||
</FiltersCol>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
Autocomplete,
|
||||
Radio,
|
||||
} from "@mui/material";
|
||||
import { fontSizeMedium, fontSizeSmall } from "../../../constants/Misc.ts";
|
||||
|
||||
export const VideoContainer = styled(Grid)(({ theme }) => ({
|
||||
position: "relative",
|
||||
@ -23,7 +24,7 @@ export const VideoContainer = styled(Grid)(({ theme }) => ({
|
||||
|
||||
export const VideoCardContainer = styled("div")(({ theme }) => ({
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(auto-fill, minmax(250px, 1fr))",
|
||||
gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))",
|
||||
gap: theme.spacing(2),
|
||||
padding: "10px",
|
||||
width: "100%",
|
||||
@ -31,7 +32,7 @@ export const VideoCardContainer = styled("div")(({ theme }) => ({
|
||||
|
||||
export const VideoCardCol = styled("div")({
|
||||
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
|
||||
// ... other styles
|
||||
});
|
||||
@ -132,16 +133,14 @@ export const FiltersCol = styled(Grid)(({ theme }) => ({
|
||||
export const FiltersContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
}));
|
||||
|
||||
export const FiltersRow = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
justifyContent: "center",
|
||||
width: "100%",
|
||||
padding: "0 15px",
|
||||
fontSize: "16px",
|
||||
fontSize: fontSizeSmall,
|
||||
userSelect: "none",
|
||||
}));
|
||||
|
||||
@ -161,7 +160,7 @@ export const FiltersRadioButton = styled(Radio)(({ theme }) => ({
|
||||
|
||||
export const FiltersSubContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
alignItems: "start",
|
||||
flexDirection: "column",
|
||||
gap: "5px",
|
||||
}));
|
||||
|
@ -145,7 +145,6 @@ export const VideoList = ({ videos }: VideoListProps) => {
|
||||
maxHeight: "50%",
|
||||
}}
|
||||
/>
|
||||
|
||||
<VideoCardTitle>{videoObj?.title}</VideoCardTitle>
|
||||
<BottomParent>
|
||||
<NameContainer
|
||||
|
@ -1,10 +1,15 @@
|
||||
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 LazyLoad from "../../components/common/LazyLoad";
|
||||
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 { FiltersCol, VideoManagerRow } from "./Components/VideoList-styles.tsx";
|
||||
import VideoList from "./Components/VideoList.tsx";
|
||||
@ -29,79 +34,77 @@ export const Home = ({ mode }: HomeProps) => {
|
||||
paddingLeft: "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 (
|
||||
<>
|
||||
<Grid container sx={{ width: "100%" }}>
|
||||
<Box sx={{ ...homeBaseSX, ...homeColumns }}>
|
||||
<SearchSidebar onSearch={getVideosHandler} />
|
||||
<Grid item xs={12} md={10} lg={7} xl={8} sm={9}>
|
||||
<VideoManagerRow>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
marginTop: "20px",
|
||||
}}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<TabContext value={tabValue}>
|
||||
<TabList
|
||||
onChange={changeTab}
|
||||
textColor={"secondary"}
|
||||
indicatorColor={"secondary"}
|
||||
centered={false}
|
||||
>
|
||||
<SubtitleContainer
|
||||
sx={{
|
||||
justifyContent: "flex-start",
|
||||
paddingLeft: "15px",
|
||||
width: "100%",
|
||||
maxWidth: "1400px",
|
||||
}}
|
||||
></SubtitleContainer>
|
||||
<TabContext value={tabValue}>
|
||||
<TabList
|
||||
onChange={changeTab}
|
||||
textColor={"secondary"}
|
||||
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}>
|
||||
<Tab label="All" value={"all"} sx={tabSX} />
|
||||
<Tab label="Subscriptions" value={"subscriptions"} sx={tabSX} />
|
||||
</TabList>
|
||||
<TabPanel value={"all"} sx={tabPaneSX}>
|
||||
<VideoList videos={videos} />
|
||||
<LazyLoad
|
||||
onLoadMore={getVideosHandler}
|
||||
isLoading={isLoading}
|
||||
></LazyLoad>
|
||||
</TabPanel>
|
||||
<TabPanel value={"subscriptions"} sx={tabPaneSX}>
|
||||
{filteredSubscriptionList.length > 0 ? (
|
||||
<>
|
||||
<VideoList videos={videos} />
|
||||
<LazyLoad
|
||||
onLoadMore={getVideosHandler}
|
||||
isLoading={isLoading}
|
||||
></LazyLoad>
|
||||
</TabPanel>
|
||||
<TabPanel value={"subscriptions"} sx={tabPaneSX}>
|
||||
{filteredSubscriptionList.length > 0 ? (
|
||||
<>
|
||||
<VideoList videos={videos} />
|
||||
<LazyLoad
|
||||
onLoadMore={getVideosHandler}
|
||||
isLoading={isLoading}
|
||||
></LazyLoad>
|
||||
</>
|
||||
) : !isLoading ? (
|
||||
<div style={{ textAlign: "center" }}>
|
||||
You have no subscriptions
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</TabPanel>
|
||||
</TabContext>
|
||||
</Box>
|
||||
</VideoManagerRow>
|
||||
</Grid>
|
||||
<FiltersCol item xs={0} lg={3} xl={2}>
|
||||
<ListSuperLikeContainer />
|
||||
</FiltersCol>
|
||||
</Grid>
|
||||
</>
|
||||
) : (
|
||||
!isLoading && (
|
||||
<div style={{ textAlign: "center" }}>
|
||||
You have no subscriptions
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</TabPanel>
|
||||
</TabContext>
|
||||
</Box>
|
||||
|
||||
<ListSuperLikeContainer />
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -19,6 +19,8 @@ interface settingsState {
|
||||
subscriptionListFilter: SubscriptionListFilterType;
|
||||
showStats: boolean;
|
||||
volume: number;
|
||||
mutedVolume: number;
|
||||
isMuted: boolean;
|
||||
}
|
||||
|
||||
const initialState: settingsState = {
|
||||
@ -30,6 +32,8 @@ const initialState: settingsState = {
|
||||
subscriptionListFilter: "currentNameOnly",
|
||||
showStats: true,
|
||||
volume: 0.5,
|
||||
mutedVolume: 0,
|
||||
isMuted: false,
|
||||
};
|
||||
|
||||
export const persistSlice = createSlice({
|
||||
@ -77,6 +81,12 @@ export const persistSlice = createSlice({
|
||||
setVolumeSetting: (state, action: PayloadAction<number>) => {
|
||||
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,
|
||||
resetSubscriptions,
|
||||
setVolumeSetting,
|
||||
setMutedVolumeSetting,
|
||||
setIsMuted,
|
||||
} = persistSlice.actions;
|
||||
|
||||
export default persistSlice.reducer;
|
||||
|
Loading…
x
Reference in New Issue
Block a user