mirror of
https://github.com/Qortal/q-tube.git
synced 2025-11-02 13:37:06 +00:00
Names Updates
This commit is contained in:
22
package-lock.json
generated
22
package-lock.json
generated
@@ -17,6 +17,7 @@
|
||||
"@reduxjs/toolkit": "^2.5.0",
|
||||
"compressorjs": "^1.2.1",
|
||||
"dompurify": "^3.2.3",
|
||||
"jotai": "^2.12.4",
|
||||
"localforage": "^1.10.0",
|
||||
"moment": "^2.30.1",
|
||||
"qapp-core": "^1.0.27",
|
||||
@@ -3519,6 +3520,27 @@
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jotai": {
|
||||
"version": "2.12.4",
|
||||
"resolved": "https://registry.npmjs.org/jotai/-/jotai-2.12.4.tgz",
|
||||
"integrity": "sha512-eFXLJol4oOLM8BS1+QV+XwaYQITG8n1tatBCFl4F5HE3zR5j2WIK8QpMt7VJIYmlogNUZfvB7wjwLoVk+umB9Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=17.0.0",
|
||||
"react": ">=17.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"@reduxjs/toolkit": "^2.5.0",
|
||||
"compressorjs": "^1.2.1",
|
||||
"dompurify": "^3.2.3",
|
||||
"jotai": "^2.12.4",
|
||||
"localforage": "^1.10.0",
|
||||
"moment": "^2.30.1",
|
||||
"qapp-core": "^1.0.29",
|
||||
|
||||
@@ -3,7 +3,6 @@ import { ThemeProvider } from "@mui/material/styles";
|
||||
import { useEffect, useState } from "react";
|
||||
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";
|
||||
import { subscriptionListFilter } from "./App-Functions.ts";
|
||||
import Notification from "./components/common/Notification/Notification";
|
||||
|
||||
32
src/components/UserDropDown.tsx
Normal file
32
src/components/UserDropDown.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Avatar, useTheme } from "@mui/material";
|
||||
import { AccountCircleSVG } from "../assets/svgs/AccountCircleSVG";
|
||||
import { menuIconSize } from "../constants/Misc";
|
||||
import { DropdownContainer, DropdownText } from "./layout/Navbar/Navbar-styles";
|
||||
|
||||
export const UserDropDown = ({ userName, handleMyChannelLink, popMenuRef }) => {
|
||||
const theme = useTheme();
|
||||
const userAvatar = `/arbitrary/THUMBNAIL/${userName}/avatar?async=true`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownContainer
|
||||
onClick={() => {
|
||||
handleMyChannelLink(userName);
|
||||
popMenuRef.current.closePopover();
|
||||
}}
|
||||
>
|
||||
{!userAvatar ? (
|
||||
<AccountCircleSVG
|
||||
color={theme.palette.text.primary}
|
||||
width={menuIconSize}
|
||||
height={menuIconSize}
|
||||
/>
|
||||
) : (
|
||||
<Avatar src={userAvatar}/>
|
||||
)}
|
||||
<DropdownText>{userName}</DropdownText>
|
||||
</DropdownContainer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,21 +9,24 @@ import {
|
||||
NavbarName,
|
||||
} from "../Navbar-styles.tsx";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import { useRef, useState } from "react";
|
||||
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import PersonOffIcon from "@mui/icons-material/PersonOff";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { PopMenu, PopMenuRefType } from "../../../common/PopMenu.tsx";
|
||||
|
||||
import { UserDropDown } from "../../../UserDropDown.tsx";
|
||||
import { Names } from "../../../../state/global/names.ts";
|
||||
export interface NavBarMenuProps {
|
||||
isShowMenu: boolean;
|
||||
userAvatar: string;
|
||||
userName: string | null;
|
||||
allNames: Names;
|
||||
}
|
||||
|
||||
export const UserMenu = ({
|
||||
isShowMenu,
|
||||
userAvatar,
|
||||
userName,
|
||||
allNames
|
||||
}: NavBarMenuProps) => {
|
||||
const isScreenSmall = !useMediaQuery(`(min-width:600px)`);
|
||||
const theme = useTheme();
|
||||
@@ -33,9 +36,10 @@ export const UserMenu = ({
|
||||
const popMenuRef = useRef<PopMenuRefType>(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleMyChannelLink = () => {
|
||||
navigate(`/channel/${userName}`);
|
||||
};
|
||||
const handleMyChannelLink = useCallback((switchToName) => {
|
||||
userName = switchToName;
|
||||
navigate(`/channel/${switchToName}`);
|
||||
}, [navigate]);
|
||||
|
||||
const onCloseBlockedNames = () => {
|
||||
setIsOpenBlockedNamesModal(false);
|
||||
@@ -43,7 +47,7 @@ export const UserMenu = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{isShowMenu && (
|
||||
{isShowMenu && (
|
||||
<>
|
||||
<PopMenu
|
||||
ref={popMenuRef}
|
||||
@@ -70,31 +74,17 @@ export const UserMenu = ({
|
||||
</AvatarContainer>
|
||||
}
|
||||
>
|
||||
<DropdownContainer
|
||||
onClick={() => {
|
||||
handleMyChannelLink();
|
||||
popMenuRef.current.closePopover();
|
||||
}}
|
||||
>
|
||||
{!userAvatar ? (
|
||||
<AccountCircleSVG
|
||||
color={theme.palette.text.primary}
|
||||
width={menuIconSize}
|
||||
height={menuIconSize}
|
||||
|
||||
{
|
||||
allNames.map((name) => (
|
||||
<UserDropDown key={name.name}
|
||||
userName={name.name}
|
||||
handleMyChannelLink={handleMyChannelLink}
|
||||
popMenuRef={popMenuRef}
|
||||
/>
|
||||
) : (
|
||||
<img
|
||||
src={userAvatar}
|
||||
alt="User Avatar"
|
||||
width={menuIconSize}
|
||||
height={menuIconSize}
|
||||
style={{
|
||||
borderRadius: "50%",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<DropdownText>{userName}</DropdownText>
|
||||
</DropdownContainer>
|
||||
))
|
||||
}
|
||||
|
||||
<DropdownContainer
|
||||
onClick={() => {
|
||||
setIsOpenBlockedNamesModal(true);
|
||||
|
||||
@@ -6,16 +6,18 @@ import { PublishMenu } from "./Components/PublishMenu.tsx";
|
||||
import { QtubeLogo } from "./Components/QtubeLogo.tsx";
|
||||
import { UserMenu } from "./Components/UserMenu.tsx";
|
||||
import { CustomAppBar } from "./Navbar-styles";
|
||||
import { Names } from "./../../../state/global/names.ts";
|
||||
|
||||
interface Props {
|
||||
isAuthenticated: boolean;
|
||||
userName: string | null;
|
||||
allNames: Names;
|
||||
userAvatar: string;
|
||||
authenticate: () => void;
|
||||
setTheme: (val: string) => void;
|
||||
}
|
||||
|
||||
const NavBar: React.FC<Props> = ({ isAuthenticated, userName, userAvatar }) => {
|
||||
const NavBar: React.FC<Props> = ({ isAuthenticated, userName, allNames, userAvatar }) => {
|
||||
const isScreenSmall = !useMediaQuery(`(min-width:600px)`);
|
||||
const isSecure = isAuthenticated && !!userName;
|
||||
const gapSize = 10;
|
||||
@@ -45,6 +47,7 @@ const NavBar: React.FC<Props> = ({ isAuthenticated, userName, userAvatar }) => {
|
||||
isShowMenu={isSecure}
|
||||
userAvatar={userAvatar}
|
||||
userName={userName}
|
||||
allNames={allNames}
|
||||
/>
|
||||
<PublishMenu isDisplayed={isSecure} />
|
||||
</Box>
|
||||
|
||||
36
src/hooks/useHandleNameData.tsx
Normal file
36
src/hooks/useHandleNameData.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { namesAtom } from '../state/global/names';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
//import { useGlobal } from 'qapp-core';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { RootState } from '../state/store';
|
||||
|
||||
export const useHandleNameData = () => {
|
||||
const setNames = useSetAtom(namesAtom);
|
||||
const user = useSelector((state: RootState) => state.auth.user);
|
||||
|
||||
const getMyNames = useCallback(async () => {
|
||||
if (!user?.address) return;
|
||||
try {
|
||||
const res = await qortalRequest({
|
||||
action: 'GET_ACCOUNT_NAMES',
|
||||
address: user.address,
|
||||
limit: 0,
|
||||
offset: 0,
|
||||
reverse: false,
|
||||
});
|
||||
setNames(res);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}, [user?.address, setNames]);
|
||||
|
||||
// Initial fetch + interval
|
||||
useEffect(() => {
|
||||
getMyNames();
|
||||
const interval = setInterval(getMyNames, 120_000); // every 2 minutes
|
||||
return () => clearInterval(interval);
|
||||
}, [getMyNames]);
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import ReactDOM from "react-dom/client";
|
||||
import App from "./App";
|
||||
import "./index.css";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
|
||||
interface CustomWindow extends Window {
|
||||
_qdnBase: string;
|
||||
}
|
||||
|
||||
@@ -4,13 +4,19 @@ import { SubscribeButton } from "../../../components/common/ContentButtons/Subsc
|
||||
import { RootState } from "../../../state/store.ts";
|
||||
import { ChannelParams } from "./ChannelName.tsx";
|
||||
import { StyledCardColComment } from "./VideoContent-styles.tsx";
|
||||
import { namesAtom } from '../../../state/global/names';
|
||||
import { useAtom } from "jotai";
|
||||
|
||||
export const ChannelButtons = ({ channelName, sx }: ChannelParams) => {
|
||||
const userName = useSelector((state: RootState) => state.auth.user?.name);
|
||||
|
||||
const [names] = useAtom(namesAtom);
|
||||
const isInNames = names.map((name) => name.name ).includes(channelName);
|
||||
|
||||
//const userName = useSelector((state: RootState) => state.auth.user?.name);
|
||||
// We need to put a change in here to get the currentUser name, not from the Redux state
|
||||
return (
|
||||
<StyledCardColComment sx={{ alignItems: "center", ...sx }}>
|
||||
{channelName !== userName && (
|
||||
{!isInNames && (
|
||||
<>
|
||||
<SubscribeButton subscriberName={channelName} />
|
||||
<FollowButton
|
||||
|
||||
8
src/state/global/names.ts
Normal file
8
src/state/global/names.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { atom } from 'jotai';
|
||||
export interface Name {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type Names = Name[];
|
||||
|
||||
export const namesAtom = atom<Names>([]);
|
||||
@@ -29,7 +29,9 @@ import ConsentModal from "../components/common/ConsentModal";
|
||||
import { useFetchSuperLikes } from "../hooks/useFetchSuperLikes";
|
||||
import { SUPER_LIKE_BASE } from "../constants/Identifiers.ts";
|
||||
import { minPriceSuperLike } from "../constants/Misc.ts";
|
||||
|
||||
import { useHandleNameData } from './../hooks/useHandleNameData.tsx';
|
||||
import { namesAtom } from './../state/global/names';
|
||||
import { useAtom } from 'jotai';
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
setTheme: (val: string) => void;
|
||||
@@ -47,15 +49,20 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
||||
const user = useSelector((state: RootState) => state.auth.user);
|
||||
const { addSuperlikeRawDataGetToList } = useFetchSuperLikes();
|
||||
const interval = useRef<any>(null);
|
||||
|
||||
useHandleNameData();
|
||||
|
||||
const videoPlaying = useSelector(
|
||||
(state: RootState) => state.global.videoPlaying
|
||||
);
|
||||
|
||||
const username = useMemo(() => {
|
||||
if (!user?.name) return "";
|
||||
|
||||
return user.name;
|
||||
}, [user]);
|
||||
|
||||
const [names] = useAtom(namesAtom);
|
||||
|
||||
const getAvatar = React.useCallback(
|
||||
async (author: string) => {
|
||||
try {
|
||||
@@ -223,6 +230,7 @@ const GlobalWrapper: React.FC<Props> = ({ children, setTheme }) => {
|
||||
setTheme={(val: string) => setTheme(val)}
|
||||
isAuthenticated={!!user?.name}
|
||||
userName={user?.name || ""}
|
||||
allNames={names}
|
||||
userAvatar={userAvatar}
|
||||
authenticate={askForAccountInformation}
|
||||
/>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 3000
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 3000
|
||||
},
|
||||
base: "",
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user