forked from Qortal/q-tube
Merge pull request #78 from QortalSeth/q-search
Added Indexing and Copy Link buttons to Video Page
This commit is contained in:
8
package-lock.json
generated
8
package-lock.json
generated
@@ -19,7 +19,7 @@
|
||||
"dompurify": "^3.2.3",
|
||||
"localforage": "^1.10.0",
|
||||
"moment": "^2.30.1",
|
||||
"qapp-core": "^1.0.15",
|
||||
"qapp-core": "^1.0.17",
|
||||
"quill": "^2.0.2",
|
||||
"quill-image-resize-module-react": "^3.0.0",
|
||||
"react": "^19.0.0",
|
||||
@@ -4066,9 +4066,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/qapp-core": {
|
||||
"version": "1.0.15",
|
||||
"resolved": "https://registry.npmjs.org/qapp-core/-/qapp-core-1.0.15.tgz",
|
||||
"integrity": "sha512-NmeAmCfiC0FzSgLApwtD6WKh2Sl6L6ZJyx6yiMNMcOYoyHWSL9AfZhS+Q+fkppzXMT5S8RV6FUMwKKplmtcCKw==",
|
||||
"version": "1.0.17",
|
||||
"resolved": "https://registry.npmjs.org/qapp-core/-/qapp-core-1.0.17.tgz",
|
||||
"integrity": "sha512-c++yhwAIlg4HhJcp7J2UNBSo/+va7I8GbtmqZ91HNHN2uk5qrwnxIQvTdWXynLvaTVrq16pW7Ry92lTncumGHg==",
|
||||
"dependencies": {
|
||||
"@tanstack/react-virtual": "^3.13.2",
|
||||
"bloom-filters": "^3.0.4",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"dompurify": "^3.2.3",
|
||||
"localforage": "^1.10.0",
|
||||
"moment": "^2.30.1",
|
||||
"qapp-core": "^1.0.15",
|
||||
"qapp-core": "^1.0.17",
|
||||
"quill": "^2.0.2",
|
||||
"quill-image-resize-module-react": "^3.0.0",
|
||||
"react": "^19.0.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CssBaseline } from "@mui/material";
|
||||
import { ThemeProvider } from "@mui/material/styles";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Provider } from "react-redux";
|
||||
import { Provider, useSelector } from "react-redux";
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
import { persistStore } from "redux-persist";
|
||||
import { PersistGate } from "redux-persist/integration/react";
|
||||
@@ -13,16 +13,18 @@ import { PlaylistContent } from "./pages/ContentPages/PlaylistContent/PlaylistCo
|
||||
import { VideoContent } from "./pages/ContentPages/VideoContent/VideoContent";
|
||||
import { Home } from "./pages/Home/Home";
|
||||
import { setFilteredSubscriptions } from "./state/features/videoSlice.ts";
|
||||
import { store, persistor } from "./state/store";
|
||||
import { store, persistor, RootState } from "./state/store";
|
||||
import { darkTheme, lightTheme } from "./styles/theme";
|
||||
import DownloadWrapper from "./wrappers/DownloadWrapper";
|
||||
import GlobalWrapper from "./wrappers/GlobalWrapper";
|
||||
import { ScrollWrapper } from "./wrappers/ScrollWrapper.tsx";
|
||||
import { QappCoreWrapper } from "./QappCoreWrapper.tsx";
|
||||
|
||||
function App() {
|
||||
// const themeColor = window._qdnTheme
|
||||
|
||||
const [theme, setTheme] = useState("dark");
|
||||
|
||||
useIframe();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -35,6 +37,7 @@ function App() {
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<ThemeProvider theme={theme === "light" ? lightTheme : darkTheme}>
|
||||
<QappCoreWrapper>
|
||||
<Notification />
|
||||
<DownloadWrapper>
|
||||
<GlobalWrapper setTheme={(val: string) => setTheme(val)}>
|
||||
@@ -55,6 +58,7 @@ function App() {
|
||||
</ScrollWrapper>
|
||||
</GlobalWrapper>
|
||||
</DownloadWrapper>
|
||||
</QappCoreWrapper>
|
||||
</ThemeProvider>
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
|
||||
25
src/QappCoreWrapper.tsx
Normal file
25
src/QappCoreWrapper.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { GlobalProvider } from 'qapp-core'
|
||||
import React from 'react'
|
||||
import { useSelector } from 'react-redux';
|
||||
import { RootState } from './state/store';
|
||||
|
||||
export const QappCoreWrapper = ({children}) => {
|
||||
const { user } = useSelector((state: RootState) => state.auth);
|
||||
return (
|
||||
<GlobalProvider
|
||||
config={{
|
||||
auth: {
|
||||
authenticateOnMount: false,
|
||||
userAccountInfo: {
|
||||
address: user?.address,
|
||||
publicKey: user?.publicKey
|
||||
}
|
||||
},
|
||||
publicSalt: "usVbeM9YpjGCbLrTcc78YJS0ap1AxDkHAOMZrp3+wDY=",
|
||||
appName: "Q-Tube",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</GlobalProvider>
|
||||
)
|
||||
}
|
||||
37
src/components/common/ContentButtons/CopyLinkButton.tsx
Normal file
37
src/components/common/ContentButtons/CopyLinkButton.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import ShareIcon from "@mui/icons-material/Share";
|
||||
import { Box, ButtonBase } from "@mui/material";
|
||||
import { CustomTooltip } from "./CustomTooltip.tsx";
|
||||
import { setNotification } from "../../../state/features/notificationsSlice";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
export interface CopyLinkButtonProps {
|
||||
link: string;
|
||||
tooltipTitle: string;
|
||||
}
|
||||
export const CopyLinkButton = ({ link, tooltipTitle }: CopyLinkButtonProps) => {
|
||||
const dispatch = useDispatch();
|
||||
return (
|
||||
<CustomTooltip title={tooltipTitle} placement={"top"} arrow>
|
||||
<Box
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(link).then(() => {
|
||||
dispatch(
|
||||
setNotification({
|
||||
msg: "Copied to clipboard!",
|
||||
alertType: "success",
|
||||
})
|
||||
);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<ShareIcon />
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
</CustomTooltip>
|
||||
);
|
||||
};
|
||||
22
src/components/common/ContentButtons/CustomTooltip.tsx
Normal file
22
src/components/common/ContentButtons/CustomTooltip.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { styled, Tooltip, tooltipClasses, TooltipProps } from "@mui/material";
|
||||
import { JSX } from "react";
|
||||
|
||||
export const TooltipLine = styled("div")(({ theme }) => ({
|
||||
fontSize: "18px",
|
||||
}));
|
||||
|
||||
const CustomWidthTooltipStyles = styled(
|
||||
({ className, ...props }: TooltipProps) => (
|
||||
<Tooltip {...props} classes={{ popper: className }} />
|
||||
)
|
||||
)({
|
||||
[`& .${tooltipClasses.tooltip}`]: {
|
||||
maxWidth: 600,
|
||||
},
|
||||
});
|
||||
|
||||
export const CustomTooltip = ({ title, ...props }: TooltipProps) => {
|
||||
if (typeof title === "string") title = <TooltipLine>{title}</TooltipLine>;
|
||||
|
||||
return <CustomWidthTooltipStyles title={title} {...props} />;
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import { Box, Button, ButtonProps } from "@mui/material";
|
||||
import Tooltip, { TooltipProps, tooltipClasses } from "@mui/material/Tooltip";
|
||||
import { MouseEvent, useEffect, useState } from "react";
|
||||
import { styled } from "@mui/material/styles";
|
||||
import { CustomTooltip, TooltipLine } from "./CustomTooltip.tsx";
|
||||
|
||||
interface FollowButtonProps extends ButtonProps {
|
||||
followerName: string;
|
||||
@@ -116,10 +117,6 @@ export const FollowButton = ({ followerName, ...props }: FollowButtonProps) => {
|
||||
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
||||
}
|
||||
|
||||
const TooltipLine = styled("div")(({ theme }) => ({
|
||||
fontSize: "18px",
|
||||
}));
|
||||
|
||||
const tooltipTitle = followingSize && (
|
||||
<>
|
||||
<TooltipLine>
|
||||
@@ -133,17 +130,9 @@ export const FollowButton = ({ followerName, ...props }: FollowButtonProps) => {
|
||||
</>
|
||||
);
|
||||
|
||||
const CustomWidthTooltip = styled(({ className, ...props }: TooltipProps) => (
|
||||
<Tooltip {...props} classes={{ popper: className }} />
|
||||
))({
|
||||
[`& .${tooltipClasses.tooltip}`]: {
|
||||
maxWidth: 600,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<CustomWidthTooltip title={tooltipTitle} placement={"top"} arrow>
|
||||
<CustomTooltip title={tooltipTitle} placement={"top"} arrow>
|
||||
<Button
|
||||
{...props}
|
||||
variant={"contained"}
|
||||
@@ -153,7 +142,7 @@ export const FollowButton = ({ followerName, ...props }: FollowButtonProps) => {
|
||||
>
|
||||
{isFollowingName() ? "Unfollow" : "Follow"}
|
||||
</Button>
|
||||
</CustomWidthTooltip>
|
||||
</CustomTooltip>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
31
src/components/common/ContentButtons/IndexButton.tsx
Normal file
31
src/components/common/ContentButtons/IndexButton.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import SavedSearchIcon from "@mui/icons-material/SavedSearch";
|
||||
import { IconButton, Tooltip } from "@mui/material";
|
||||
import { createQortalLink, IndexCategory, useGlobal } from "qapp-core";
|
||||
import { CustomTooltip } from "./CustomTooltip.tsx";
|
||||
|
||||
export interface IndexButtonProps {
|
||||
channelName: string;
|
||||
}
|
||||
|
||||
export const IndexButton = ({ channelName }: IndexButtonProps) => {
|
||||
const openPageIndexManager = useGlobal().indexOperations.openPageIndexManager;
|
||||
|
||||
return (
|
||||
<CustomTooltip title={`Index Video`} arrow placement={"top"}>
|
||||
<IconButton
|
||||
sx={{ padding: 0 }}
|
||||
onClick={() => {
|
||||
const link = createQortalLink("APP", "Q-Tube", location.pathname);
|
||||
openPageIndexManager({
|
||||
link: link,
|
||||
name: channelName,
|
||||
category: IndexCategory.PUBLIC_PAGE_VIDEO,
|
||||
rootName: "Q-Tube",
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SavedSearchIcon fontSize={"large"} htmlColor={"#00C1E8"} />
|
||||
</IconButton>
|
||||
</CustomTooltip>
|
||||
);
|
||||
};
|
||||
@@ -10,6 +10,7 @@ import ShortUniqueId from "short-unique-id";
|
||||
import { objectToBase64 } from "../../../utils/PublishFormatter.ts";
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { FOR, FOR_LIKE, LIKE_BASE } from "../../../constants/Identifiers.ts";
|
||||
import { CustomTooltip } from "./CustomTooltip.tsx";
|
||||
import {
|
||||
formatLikeCount,
|
||||
getCurrentLikesAndDislikesCount,
|
||||
@@ -155,7 +156,7 @@ export const LikeAndDislike = ({ name, identifier }: LikeAndDislikeProps) => {
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<Tooltip title="Like or Dislike Video" placement="top">
|
||||
<CustomTooltip title="Like or Dislike Video" placement="top">
|
||||
<Box
|
||||
sx={{
|
||||
padding: "5px",
|
||||
@@ -219,7 +220,7 @@ export const LikeAndDislike = ({ name, identifier }: LikeAndDislikeProps) => {
|
||||
</div>
|
||||
)}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</CustomTooltip>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from "../../../state/features/persistSlice.ts";
|
||||
import { setFilteredSubscriptions } from "../../../state/features/videoSlice.ts";
|
||||
import { styled } from "@mui/material/styles";
|
||||
import { CustomTooltip, TooltipLine } from "./CustomTooltip.tsx";
|
||||
|
||||
interface SubscribeButtonProps extends ButtonProps {
|
||||
subscriberName: string;
|
||||
@@ -94,10 +95,6 @@ export const SubscribeButton = ({
|
||||
...props.sx,
|
||||
};
|
||||
|
||||
const TooltipLine = styled("div")(({ theme }) => ({
|
||||
fontSize: "18px",
|
||||
}));
|
||||
|
||||
const tooltipTitle = (
|
||||
<>
|
||||
<TooltipLine>
|
||||
@@ -108,7 +105,7 @@ export const SubscribeButton = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip title={tooltipTitle} placement={"top"} arrow>
|
||||
<CustomTooltip title={tooltipTitle} placement={"top"} arrow>
|
||||
<Button
|
||||
{...props}
|
||||
variant={"contained"}
|
||||
@@ -118,6 +115,6 @@ export const SubscribeButton = ({
|
||||
>
|
||||
{isSubscribed ? "Unsubscribe" : "Subscribe"}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</CustomTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
} from "../../Publish/PublishVideo/PublishVideo-styles.tsx";
|
||||
import { CommentInput } from "../Comments/Comments-styles.tsx";
|
||||
import { hashWordWithoutPublicSalt } from "qapp-core";
|
||||
import { CustomTooltip } from "./CustomTooltip.tsx";
|
||||
|
||||
const uid = new ShortUniqueId({ length: 7 });
|
||||
|
||||
@@ -110,19 +111,16 @@ export const SuperLike = ({
|
||||
destinationAddress: address,
|
||||
amount: superlikeDonationAmount,
|
||||
});
|
||||
|
||||
const hashPostId = await hashWordWithoutPublicSalt(identifier, 20)
|
||||
|
||||
const hashPostId = await hashWordWithoutPublicSalt(identifier, 20);
|
||||
|
||||
const metadescription = `**sig:${
|
||||
res?.signature
|
||||
};${FOR}:${name}_${FOR_SUPER_LIKE};nm:${name.slice(
|
||||
0,
|
||||
20
|
||||
)}**`;
|
||||
};${FOR}:${name}_${FOR_SUPER_LIKE};nm:${name.slice(0, 20)}**`;
|
||||
const id = uid.rnd();
|
||||
|
||||
const identifierSuperLike = `${SUPER_LIKE_BASE}${hashPostId}_${id}`;
|
||||
|
||||
|
||||
const superLikeToBase64 = await objectToBase64({
|
||||
comment,
|
||||
transactionReference: res?.signature,
|
||||
@@ -204,7 +202,7 @@ export const SuperLike = ({
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<Tooltip title="Super Like" placement="top">
|
||||
<CustomTooltip title="Super Like" placement="top">
|
||||
<Box
|
||||
sx={{
|
||||
padding: "5px",
|
||||
@@ -248,7 +246,7 @@ export const SuperLike = ({
|
||||
</div>
|
||||
)}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</CustomTooltip>
|
||||
</Box>
|
||||
<Modal
|
||||
open={isOpen}
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
import DownloadIcon from "@mui/icons-material/Download";
|
||||
import { Box, ButtonBase, SxProps, Theme, useMediaQuery } from "@mui/material";
|
||||
import { Box, SxProps, Theme } from "@mui/material";
|
||||
import { useMemo } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { CopyLinkButton } from "../../../components/common/ContentButtons/CopyLinkButton.tsx";
|
||||
import { IndexButton } from "../../../components/common/ContentButtons/IndexButton.tsx";
|
||||
import { LikeAndDislike } from "../../../components/common/ContentButtons/LikeAndDislike.tsx";
|
||||
import { SuperLike } from "../../../components/common/ContentButtons/SuperLike.tsx";
|
||||
import Tooltip, { TooltipProps, tooltipClasses } from "@mui/material/Tooltip";
|
||||
import ShareIcon from '@mui/icons-material/Share'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { setNotification } from '../../../state/features/notificationsSlice'
|
||||
import FileElement from "../../../components/common/FileElement.tsx";
|
||||
import {
|
||||
smallScreenSizeString,
|
||||
titleFormatterOnSave,
|
||||
} from "../../../constants/Misc.ts";
|
||||
import { titleFormatterOnSave } from "../../../constants/Misc.ts";
|
||||
import { ChannelActions } from "./ChannelActions.tsx";
|
||||
import {
|
||||
FileAttachmentContainer,
|
||||
@@ -27,6 +23,10 @@ export interface VideoActionsBarProps {
|
||||
sx?: SxProps<Theme>;
|
||||
}
|
||||
|
||||
function replaceAppNameInQortalUrl(url: string, newAppName: string): string {
|
||||
return url.replace(/(qortal:\/\/APP\/)[^/]+/, `$1${newAppName}`);
|
||||
}
|
||||
|
||||
export const VideoActionsBar = ({
|
||||
channelName,
|
||||
videoData,
|
||||
@@ -48,7 +48,7 @@ export const VideoActionsBar = ({
|
||||
return superLikeList?.length ?? 0;
|
||||
}, [superLikeList]);
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const saveAsFilename = useMemo(() => {
|
||||
// nb. we prefer to construct the local filename to use for
|
||||
@@ -114,30 +114,14 @@ export const VideoActionsBar = ({
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
<Tooltip title={`Copy video link`} arrow>
|
||||
<Box
|
||||
sx={{
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(`qortal://APP/Q-Tube/video/${videoData?.user}/${videoData?.id}`).then(() => {
|
||||
dispatch(
|
||||
setNotification({
|
||||
msg: 'Copied to clipboard!',
|
||||
alertType: 'success'
|
||||
})
|
||||
)
|
||||
})
|
||||
}}
|
||||
>
|
||||
<ShareIcon />
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
|
||||
</Box>
|
||||
<Box sx={{ display: "flex", gap: "5px" }}>
|
||||
<IndexButton channelName={channelName} />
|
||||
<CopyLinkButton
|
||||
link={`qortal://APP/Q-Tube/video/${videoData?.user}/${videoData?.id}`}
|
||||
tooltipTitle={`Copy video link`}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{videoData && (
|
||||
<FileAttachmentContainer sx={{ width: "100%", maxWidth: "340px" }}>
|
||||
|
||||
Reference in New Issue
Block a user