Merge pull request #78 from QortalSeth/q-search

Added Indexing and Copy Link buttons to Video Page
This commit is contained in:
2025-05-10 12:10:20 -06:00
committed by GitHub
12 changed files with 160 additions and 72 deletions

8
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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
View 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>
)
}

View 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>
);
};

View 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} />;
};

View File

@@ -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>
</>
);
};

View 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>
);
};

View File

@@ -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>
</>
);

View File

@@ -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>
);
};

View File

@@ -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}

View File

@@ -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" }}>