Names Updates

This commit is contained in:
MergeMerc
2025-05-22 07:18:26 -04:00
parent 871ab5fb67
commit 427ae7332a
12 changed files with 148 additions and 41 deletions

22
package-lock.json generated
View File

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

View File

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

View File

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

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

View File

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

View File

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

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

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
import { atom } from 'jotai';
export interface Name {
name: string;
}
export type Names = Name[];
export const namesAtom = atom<Names>([]);

View File

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

View File

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