diff --git a/package.json b/package.json index 90535ef..0e9e6af 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "qtube", "private": true, - "version": "2.0.0", + "version": "2.1.0", "type": "module", "scripts": { "dev": "vite", diff --git a/src/components/Playlists/Playlists.tsx b/src/components/Playlists/Playlists.tsx index 0bf6ddc..d514927 100644 --- a/src/components/Playlists/Playlists.tsx +++ b/src/components/Playlists/Playlists.tsx @@ -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 ( {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}`) }} diff --git a/src/components/Publish/PublishVideo/PublishVideo.tsx b/src/components/Publish/PublishVideo/PublishVideo.tsx index a62642f..372294e 100644 --- a/src/components/Publish/PublishVideo/PublishVideo.tsx +++ b/src/components/Publish/PublishVideo/PublishVideo.tsx @@ -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 => new Promise((resolve, reject) => { @@ -82,13 +83,14 @@ export const toBase64 = (file: File): Promise => 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 : ( } + startIcon={ + + } onClick={() => { setIsOpen(true); }} > - add video + Video )} diff --git a/src/components/common/Comments/Comments-styles.tsx b/src/components/common/Comments/Comments-styles.tsx index 9cda045..34396e2 100644 --- a/src/components/common/Comments/Comments-styles.tsx +++ b/src/components/common/Comments/Comments-styles.tsx @@ -159,7 +159,7 @@ export const BlockIconContainer = styled(Box)({ }); export const CommentsContainer = styled(Box)({ - width: "70%", + width: "90%", maxWidth: "1000px", display: "flex", flexDirection: "column", diff --git a/src/components/common/ContentButtons/LikeAndDislike.tsx b/src/components/common/ContentButtons/LikeAndDislike.tsx index ebf8912..29e97c7 100644 --- a/src/components/common/ContentButtons/LikeAndDislike.tsx +++ b/src/components/common/ContentButtons/LikeAndDislike.tsx @@ -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", }} > diff --git a/src/components/common/ContentButtons/SuperLike.tsx b/src/components/common/ContentButtons/SuperLike.tsx index 7e2c4db..60d2b17 100644 --- a/src/components/common/ContentButtons/SuperLike.tsx +++ b/src/components/common/ContentButtons/SuperLike.tsx @@ -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 }); diff --git a/src/components/common/DownloadTaskManager.tsx b/src/components/common/DownloadTaskManager.tsx index 6ea28d6..c483b2d 100644 --- a/src/components/common/DownloadTaskManager.tsx +++ b/src/components/common/DownloadTaskManager.tsx @@ -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(null); - - const [openDownload, setOpenDownload] = useState(false); - + const hashMapVideos = useSelector( + (state: RootState) => state.video.hashMapVideos + ); const handleClick = (event?: React.MouseEvent) => { 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 ( - - - { + handleClick(e); + setOpenDownload(true); + }} + > + {downloadInProgress ? ( + + ) : ( + + )} + + + + - - {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 ( - { - const id = downloadObj?.properties?.jsonId - if (!id) return - - navigate( - `/video/${downloadObj?.properties?.user}/${id}` - ) - }} - > - - - {service === 'VIDEO' && ( - - )} - + {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; - - - - - {`${progress?.toFixed(0)}%`}{' '} - {status && status === 'REFETCHING' && '- refetching'} - {status && status === 'DOWNLOADED' && '- building'} - - - { + const userName = downloadObj?.name; + const identifier = downloadObj?.identifier; + + if (identifier && userName) + navigate(`/video/${userName}/${identifier}_metadata`); + }} + > + + + {service === "VIDEO" && ( + + )} + + + + - {downloadObj?.identifier} - - - ) - })} - - - + /> + + + {`${progress?.toFixed(0)}%`}{" "} + {status && status === "REFETCHING" && "- refetching"} + {status && status === "DOWNLOADED" && "- building"} + + + + {videoTitle || downloadObj?.identifier} + + + ); + })} + + - - ) -} + ); +}; diff --git a/src/components/common/ListSuperLikes/ListSuperLikeContainer.tsx b/src/components/common/ListSuperLikes/ListSuperLikeContainer.tsx index 4a7cc53..3afd50c 100644 --- a/src/components/common/ListSuperLikes/ListSuperLikeContainer.tsx +++ b/src/components/common/ListSuperLikes/ListSuperLikeContainer.tsx @@ -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 ( - + const isScreenLarge = useMediaQuery("(min-width:1200px)"); + const superlikeListComponent = ( + <> { Recent Super likes + + ); + + // @ts-ignore + return ( + + {isScreenLarge ? ( + <>{superlikeListComponent} + ) : ( + + + + + + } + > + {superlikeListComponent} + + )} ); }; diff --git a/src/components/common/Notifications/Notifications.tsx b/src/components/common/Notifications/Notifications.tsx index 4568f8e..654fa3e 100644 --- a/src/components/common/Notifications/Notifications.tsx +++ b/src/components/common/Notifications/Notifications.tsx @@ -64,7 +64,6 @@ export function extractIdValue(metadescription) { } export const Notifications = () => { - const dispatch = useDispatch(); const [anchorElNotification, setAnchorElNotification] = useState(null); const [notifications, setNotifications] = useState([]); @@ -238,7 +237,7 @@ export const Notifications = () => { badgeContent={notificationBadgeLength} color="primary" sx={{ - margin: "0px 12px", + margin: "0px", }} > - + ); }; diff --git a/src/pages/Home/Components/VideoList-styles.tsx b/src/pages/Home/Components/VideoList-styles.tsx index 3a0e48d..eb82aa2 100644 --- a/src/pages/Home/Components/VideoList-styles.tsx +++ b/src/pages/Home/Components/VideoList-styles.tsx @@ -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", })); diff --git a/src/pages/Home/Components/VideoList.tsx b/src/pages/Home/Components/VideoList.tsx index 725fadd..5e34386 100644 --- a/src/pages/Home/Components/VideoList.tsx +++ b/src/pages/Home/Components/VideoList.tsx @@ -145,7 +145,6 @@ export const VideoList = ({ videos }: VideoListProps) => { maxHeight: "50%", }} /> - {videoObj?.title} { 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 ( <> - + - - - + + - - - - - - - + + + + + + + + + {filteredSubscriptionList.length > 0 ? ( + <> - - - {filteredSubscriptionList.length > 0 ? ( - <> - - - - ) : !isLoading ? ( -
- You have no subscriptions -
- ) : ( - <> - )} -
-
-
-
-
- - - -
+ + ) : ( + !isLoading && ( +
+ You have no subscriptions +
+ ) + )} + + + + + + ); }; diff --git a/src/state/features/persistSlice.ts b/src/state/features/persistSlice.ts index 6c223ee..c2e2c6f 100644 --- a/src/state/features/persistSlice.ts +++ b/src/state/features/persistSlice.ts @@ -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) => { state.volume = action.payload; }, + setMutedVolumeSetting: (state, action: PayloadAction) => { + state.mutedVolume = action.payload; + }, + setIsMuted: (state, action: PayloadAction) => { + state.isMuted = action.payload; + }, }, }); @@ -89,6 +99,8 @@ export const { changeFilterType, resetSubscriptions, setVolumeSetting, + setMutedVolumeSetting, + setIsMuted, } = persistSlice.actions; export default persistSlice.reducer;