mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-04-29 06:17:52 +00:00
Merge pull request #16 from nbenaglia/feature/refactor-css-themes
Refactor css themes in main pages
This commit is contained in:
commit
9bf00a2713
23
.prettierrc
Normal file
23
.prettierrc
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"arrowParens": "always",
|
||||
"bracketSameLine": false,
|
||||
"bracketSpacing": true,
|
||||
"embeddedLanguageFormatting": "auto",
|
||||
"endOfLine": "lf",
|
||||
"experimentalTernaries": false,
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"insertPragma": false,
|
||||
"jsxBracketSameLine": false,
|
||||
"jsxSingleQuote": false,
|
||||
"printWidth": 80,
|
||||
"proseWrap": "preserve",
|
||||
"quoteProps": "as-needed",
|
||||
"requirePragma": false,
|
||||
"semi": true,
|
||||
"singleAttributePerLine": false,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": false,
|
||||
"vueIndentScriptAndStyle": false
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,258 +0,0 @@
|
||||
import { Typography, Box, TextField, InputLabel } from "@mui/material";
|
||||
import { styled } from "@mui/system";
|
||||
|
||||
export const AppContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
width: "100vw",
|
||||
background: "rgba(39, 40, 44, 1)",
|
||||
height: "100vh",
|
||||
radius: "15px",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
overflow: 'hidden'
|
||||
}));
|
||||
|
||||
export const AuthenticatedContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
justifyContent: "space-between",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AuthenticatedContainerInnerLeft = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AuthenticatedContainerInnerRight = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
width: "60px",
|
||||
height: "100%",
|
||||
background: "rgba(0, 0, 0, 0.1)",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AuthenticatedContainerInnerTop = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
width: "100%px",
|
||||
height: "60px",
|
||||
background: "rgba(0, 0, 0, 0.1)",
|
||||
padding: "20px",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const TextP = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "13px",
|
||||
fontWeight: 600,
|
||||
fontFamily: "Inter",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const TextItalic = styled("span")(({ theme }) => ({
|
||||
fontSize: "13px",
|
||||
fontWeight: 600,
|
||||
fontFamily: "Inter",
|
||||
fontStyle: "italic",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const TextSpan = styled("span")(({ theme }) => ({
|
||||
fontSize: "13px",
|
||||
fontFamily: "Inter",
|
||||
fontWeight: 800,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AddressBox = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
border: `1px solid ${
|
||||
theme.palette.mode === "dark"
|
||||
? "rgba(255, 255, 255, 0.5)"
|
||||
: "rgba(0, 0, 0, 0.3)"
|
||||
}`,
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "auto",
|
||||
height: "25px",
|
||||
padding: "5px 15px",
|
||||
gap: "5px",
|
||||
borderRadius: "100px",
|
||||
fontFamily: "Inter",
|
||||
fontSize: "12px",
|
||||
fontWeight: 600,
|
||||
lineHeight: "14.52px",
|
||||
textAlign: "left",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
cursor: "pointer",
|
||||
transition: "all 0.2s",
|
||||
|
||||
"&:hover": {
|
||||
backgroundColor:
|
||||
theme.palette.mode === "dark"
|
||||
? "rgba(41, 41, 43, 1)"
|
||||
: "rgba(240, 240, 240, 1)",
|
||||
color: theme.palette.mode === "dark" ? "#fff" : "#000",
|
||||
|
||||
"svg path": {
|
||||
fill: theme.palette.mode === "dark" ? "#fff" : "#000",
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export const CustomButton = styled(Box)(({ theme }) => ({
|
||||
boxSizing: "border-box",
|
||||
padding: "15px 20px",
|
||||
gap: "10px",
|
||||
|
||||
border: `0.5px solid ${
|
||||
theme.palette.mode === "dark"
|
||||
? "rgba(255, 255, 255, 0.5)"
|
||||
: "rgba(0, 0, 0, 0.3)"
|
||||
}`,
|
||||
filter: "drop-shadow(1px 4px 10.5px rgba(0, 0, 0, 0.3))",
|
||||
borderRadius: "5px",
|
||||
|
||||
display: "inline-flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
|
||||
width: "fit-content",
|
||||
minWidth: "160px",
|
||||
cursor: "pointer",
|
||||
transition: "all 0.2s",
|
||||
|
||||
fontWeight: 600,
|
||||
fontFamily: "Inter",
|
||||
textAlign: "center",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
|
||||
"&:hover": {
|
||||
backgroundColor:
|
||||
theme.palette.mode === "dark"
|
||||
? "rgba(41, 41, 43, 1)"
|
||||
: "rgba(230, 230, 230, 1)",
|
||||
color: "#fff",
|
||||
|
||||
"svg path": {
|
||||
fill: "#fff",
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
interface CustomButtonProps {
|
||||
bgColor?: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export const CustomButtonAccept = styled(Box)<CustomButtonProps>(
|
||||
({ bgColor, color, theme }) => ({
|
||||
boxSizing: "border-box",
|
||||
padding: "15px 20px",
|
||||
gap: "10px",
|
||||
border: `0.5px solid ${
|
||||
theme.palette.mode === "dark"
|
||||
? "rgba(255, 255, 255, 0.5)"
|
||||
: "rgba(0, 0, 0, 0.3)"
|
||||
}`,
|
||||
filter: "drop-shadow(1px 4px 10.5px rgba(0,0,0,0.3))",
|
||||
borderRadius: 5,
|
||||
display: "inline-flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
width: "fit-content",
|
||||
transition: "all 0.2s",
|
||||
minWidth: 160,
|
||||
cursor: "pointer",
|
||||
fontWeight: 600,
|
||||
fontFamily: "Inter",
|
||||
textAlign: "center",
|
||||
opacity: 0.7,
|
||||
|
||||
// Color and backgroundColor with fallbacks
|
||||
backgroundColor: bgColor || (theme.palette.mode === "dark" ? "#1d1d1d" : "#f5f5f5"),
|
||||
color: color || (theme.palette.mode === "dark" ? "#fff" : "#000"),
|
||||
|
||||
"&:hover": {
|
||||
opacity: 1,
|
||||
backgroundColor: bgColor || (theme.palette.mode === "dark" ? "rgba(41, 41, 43, 1)" : "rgba(230, 230, 230, 1)"),
|
||||
color: color || "#fff",
|
||||
svg: {
|
||||
path: {
|
||||
fill: color || "#fff",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
export const CustomInput = styled(TextField)(({ theme }) => ({
|
||||
width: "183px", // Adjust the width as needed
|
||||
borderRadius: "5px",
|
||||
// backgroundColor: "rgba(30, 30, 32, 1)",
|
||||
outline: "none",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
input: {
|
||||
fontSize: 10,
|
||||
fontFamily: "Inter",
|
||||
fontWeight: 400,
|
||||
color: "white",
|
||||
"&::placeholder": {
|
||||
fontSize: 16,
|
||||
color: "rgba(255, 255, 255, 0.2)",
|
||||
},
|
||||
outline: "none",
|
||||
padding: "10px",
|
||||
},
|
||||
"& .MuiOutlinedInput-root": {
|
||||
"& fieldset": {
|
||||
border: "0.5px solid rgba(255, 255, 255, 0.5)",
|
||||
},
|
||||
"&:hover fieldset": {
|
||||
border: "0.5px solid rgba(255, 255, 255, 0.5)",
|
||||
},
|
||||
"&.Mui-focused fieldset": {
|
||||
border: "0.5px solid rgba(255, 255, 255, 0.5)",
|
||||
},
|
||||
},
|
||||
"& .MuiInput-underline:before": {
|
||||
borderBottom: "none",
|
||||
},
|
||||
"& .MuiInput-underline:hover:not(.Mui-disabled):before": {
|
||||
borderBottom: "none",
|
||||
},
|
||||
"& .MuiInput-underline:after": {
|
||||
borderBottom: "none",
|
||||
},
|
||||
}));
|
||||
|
||||
export const CustomLabel = styled(InputLabel)(({ theme }) => ({
|
||||
fontWeight: 400,
|
||||
fontFamily: "Inter",
|
||||
fontSize: "10px",
|
||||
lineHeight: "12px",
|
||||
color:
|
||||
theme.palette.mode === "dark"
|
||||
? "rgba(255, 255, 255, 0.5)"
|
||||
: "rgba(0, 0, 0, 0.5)",
|
||||
}));
|
4796
src/App.tsx
4796
src/App.tsx
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
625
src/Wallets.tsx
625
src/Wallets.tsx
@ -1,48 +1,58 @@
|
||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import Divider from "@mui/material/Divider";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import ListItemAvatar from "@mui/material/ListItemAvatar";
|
||||
import Avatar from "@mui/material/Avatar";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { Box, Button, ButtonBase, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Input } from "@mui/material";
|
||||
import { CustomButton } from "./App-styles";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import EditIcon from "@mui/icons-material/Edit";
|
||||
import { Label } from "./components/Group/AddGroup";
|
||||
import { Spacer } from "./common/Spacer";
|
||||
import { getWallets, storeWallets, walletVersion } from "./background";
|
||||
import { useModal } from "./common/useModal";
|
||||
import PhraseWallet from "./utils/generateWallet/phrase-wallet";
|
||||
import { decryptStoredWalletFromSeedPhrase } from "./utils/decryptWallet";
|
||||
import { crypto } from "./constants/decryptWallet";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import { PasswordField } from "./components";
|
||||
import { HtmlTooltip } from "./ExtStates/NotAuthenticated";
|
||||
import { GlobalContext } from "./App";
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import List from '@mui/material/List';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import ListItemAvatar from '@mui/material/ListItemAvatar';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ButtonBase,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
IconButton,
|
||||
Input,
|
||||
} from '@mui/material';
|
||||
import { CustomButton } from './styles/App-styles';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import { Label } from './components/Group/AddGroup';
|
||||
import { Spacer } from './common/Spacer';
|
||||
import { getWallets, storeWallets, walletVersion } from './background';
|
||||
import { useModal } from './common/useModal';
|
||||
import PhraseWallet from './utils/generateWallet/phrase-wallet';
|
||||
import { decryptStoredWalletFromSeedPhrase } from './utils/decryptWallet';
|
||||
import { crypto } from './constants/decryptWallet';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { PasswordField } from './components';
|
||||
import { HtmlTooltip } from './ExtStates/NotAuthenticated';
|
||||
import { GlobalContext } from './App';
|
||||
|
||||
const parsefilenameQortal = (filename)=> {
|
||||
return filename.startsWith("qortal_backup_") ? filename.slice(14) : filename;
|
||||
}
|
||||
const parsefilenameQortal = (filename) => {
|
||||
return filename.startsWith('qortal_backup_') ? filename.slice(14) : filename;
|
||||
};
|
||||
|
||||
export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
||||
const [wallets, setWallets] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [seedValue, setSeedValue] = useState("");
|
||||
const [seedName, setSeedName] = useState("");
|
||||
const [seedError, setSeedError] = useState("");
|
||||
const { hasSeenGettingStarted } = useContext(GlobalContext);
|
||||
const [seedValue, setSeedValue] = useState('');
|
||||
const [seedName, setSeedName] = useState('');
|
||||
const [seedError, setSeedError] = useState('');
|
||||
const { hasSeenGettingStarted } = useContext(GlobalContext);
|
||||
|
||||
const [password, setPassword] = useState("");
|
||||
const [password, setPassword] = useState('');
|
||||
const [isOpenSeedModal, setIsOpenSeedModal] = useState(false);
|
||||
const [isLoadingEncryptSeed, setIsLoadingEncryptSeed] = useState(false);
|
||||
|
||||
const { isShow, onCancel, onOk, show, } = useModal();
|
||||
const { isShow, onCancel, onOk, show } = useModal();
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
accept: {
|
||||
"application/json": [".json"], // Only accept JSON files
|
||||
'application/json': ['.json'], // Only accept JSON files
|
||||
},
|
||||
onDrop: async (acceptedFiles) => {
|
||||
const files: any = acceptedFiles;
|
||||
@ -53,8 +63,8 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
||||
const fileContents = await new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onabort = () => reject("File reading was aborted");
|
||||
reader.onerror = () => reject("File reading has failed");
|
||||
reader.onabort = () => reject('File reading was aborted');
|
||||
reader.onerror = () => reject('File reading has failed');
|
||||
reader.onload = () => {
|
||||
// Resolve the promise with the reader result when reading completes
|
||||
resolve(reader.result);
|
||||
@ -63,9 +73,9 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
||||
// Read the file as text
|
||||
reader.readAsText(file);
|
||||
});
|
||||
if (typeof fileContents !== "string") continue;
|
||||
const parsedData = JSON.parse(fileContents)
|
||||
importedWallets.push({...parsedData, filename: file?.name});
|
||||
if (typeof fileContents !== 'string') continue;
|
||||
const parsedData = JSON.parse(fileContents);
|
||||
importedWallets.push({ ...parsedData, filename: file?.name });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
@ -108,83 +118,85 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleSetSeedValue = async ()=> {
|
||||
const handleSetSeedValue = async () => {
|
||||
try {
|
||||
setIsOpenSeedModal(true)
|
||||
const {seedValue, seedName, password} = await show({
|
||||
message: "",
|
||||
publishFee: "",
|
||||
setIsOpenSeedModal(true);
|
||||
const { seedValue, seedName, password } = await show({
|
||||
message: '',
|
||||
publishFee: '',
|
||||
});
|
||||
setIsLoadingEncryptSeed(true)
|
||||
const res = await decryptStoredWalletFromSeedPhrase(seedValue)
|
||||
setIsLoadingEncryptSeed(true);
|
||||
const res = await decryptStoredWalletFromSeedPhrase(seedValue);
|
||||
const wallet2 = new PhraseWallet(res, walletVersion);
|
||||
const wallet = await wallet2.generateSaveWalletData(
|
||||
password,
|
||||
crypto.kdfThreads,
|
||||
() => {}
|
||||
);
|
||||
if(wallet?.address0){
|
||||
setWallets([...wallets, {
|
||||
...wallet,
|
||||
name: seedName
|
||||
}]);
|
||||
setIsOpenSeedModal(false)
|
||||
setSeedValue('')
|
||||
setSeedName('')
|
||||
setPassword('')
|
||||
setSeedError('')
|
||||
if (wallet?.address0) {
|
||||
setWallets([
|
||||
...wallets,
|
||||
{
|
||||
...wallet,
|
||||
name: seedName,
|
||||
},
|
||||
]);
|
||||
setIsOpenSeedModal(false);
|
||||
setSeedValue('');
|
||||
setSeedName('');
|
||||
setPassword('');
|
||||
setSeedError('');
|
||||
} else {
|
||||
setSeedError('Could not create account.')
|
||||
setSeedError('Could not create account.');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
setSeedError(error?.message || 'Could not create account.')
|
||||
setSeedError(error?.message || 'Could not create account.');
|
||||
} finally {
|
||||
setIsLoadingEncryptSeed(false)
|
||||
setIsLoadingEncryptSeed(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const selectedWalletFunc = (wallet) => {
|
||||
setRawWallet(wallet);
|
||||
setExtState("wallet-dropped");
|
||||
setExtState('wallet-dropped');
|
||||
};
|
||||
|
||||
useEffect(()=> {
|
||||
setIsLoading(true)
|
||||
getWallets().then((res)=> {
|
||||
|
||||
if(res && Array.isArray(res)){
|
||||
setWallets(res)
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
getWallets()
|
||||
.then((res) => {
|
||||
if (res && Array.isArray(res)) {
|
||||
setWallets(res);
|
||||
}
|
||||
setIsLoading(false)
|
||||
}).catch((error)=> {
|
||||
console.error(error)
|
||||
setIsLoading(false)
|
||||
})
|
||||
}, [])
|
||||
setIsLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
setIsLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(()=> {
|
||||
if(!isLoading && wallets && Array.isArray(wallets)){
|
||||
storeWallets(wallets)
|
||||
useEffect(() => {
|
||||
if (!isLoading && wallets && Array.isArray(wallets)) {
|
||||
storeWallets(wallets);
|
||||
}
|
||||
}, [wallets, isLoading])
|
||||
}, [wallets, isLoading]);
|
||||
|
||||
if(isLoading) return null
|
||||
if (isLoading) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{(wallets?.length === 0 ||
|
||||
!wallets) ? (
|
||||
<>
|
||||
<Typography>No accounts saved</Typography>
|
||||
<Spacer height="75px" />
|
||||
</>
|
||||
): (
|
||||
<>
|
||||
<Typography>Your saved accounts</Typography>
|
||||
<Spacer height="30px" />
|
||||
</>
|
||||
)}
|
||||
{wallets?.length === 0 || !wallets ? (
|
||||
<>
|
||||
<Typography>No accounts saved</Typography>
|
||||
<Spacer height="75px" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Typography>Your saved accounts</Typography>
|
||||
<Spacer height="30px" />
|
||||
</>
|
||||
)}
|
||||
|
||||
{rawWallet && (
|
||||
<Box>
|
||||
@ -196,174 +208,198 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => {
|
||||
</Box>
|
||||
)}
|
||||
{wallets?.length > 0 && (
|
||||
<List
|
||||
sx={{
|
||||
width: "100%",
|
||||
maxWidth: "500px",
|
||||
maxHeight: "60vh",
|
||||
overflowY: "auto",
|
||||
overflowX: "hidden",
|
||||
backgroundColor: "rgb(30 30 32 / 70%)",
|
||||
|
||||
}}
|
||||
>
|
||||
{wallets?.map((wallet, idx) => {
|
||||
return (
|
||||
<>
|
||||
<WalletItem
|
||||
setSelectedWallet={selectedWalletFunc}
|
||||
key={wallet?.address0}
|
||||
wallet={wallet}
|
||||
idx={idx}
|
||||
updateWalletItem={updateWalletItem}
|
||||
/>
|
||||
<Divider variant="inset" component="li" />
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
<List
|
||||
sx={{
|
||||
width: '100%',
|
||||
maxWidth: '500px',
|
||||
maxHeight: '60vh',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
backgroundColor: 'rgb(30 30 32 / 70%)',
|
||||
}}
|
||||
>
|
||||
{wallets?.map((wallet, idx) => {
|
||||
return (
|
||||
<>
|
||||
<WalletItem
|
||||
setSelectedWallet={selectedWalletFunc}
|
||||
key={wallet?.address0}
|
||||
wallet={wallet}
|
||||
idx={idx}
|
||||
updateWalletItem={updateWalletItem}
|
||||
/>
|
||||
<Divider variant="inset" component="li" />
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
)}
|
||||
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
alignItems: 'center',
|
||||
position: wallets?.length === 0 ? 'relative' : 'fixed',
|
||||
bottom: wallets?.length === 0 ? 'unset' : '20px',
|
||||
right: wallets?.length === 0 ? 'unset' : '20px'
|
||||
bottom: wallets?.length === 0 ? 'unset' : '20px',
|
||||
right: wallets?.length === 0 ? 'unset' : '20px',
|
||||
}}
|
||||
>
|
||||
<HtmlTooltip
|
||||
disableHoverListener={hasSeenGettingStarted === true}
|
||||
|
||||
title={
|
||||
<React.Fragment>
|
||||
<Typography color="inherit" sx={{
|
||||
fontSize: '16px'
|
||||
}}>Already have a Qortal account? Enter your secret backup phrase here to access it. This phrase is one of the ways to recover your account.</Typography>
|
||||
</React.Fragment>
|
||||
}
|
||||
>
|
||||
<CustomButton onClick={handleSetSeedValue} sx={{
|
||||
padding: '10px'
|
||||
}} >
|
||||
|
||||
Add seed-phrase
|
||||
</CustomButton>
|
||||
</HtmlTooltip>
|
||||
<HtmlTooltip
|
||||
disableHoverListener={hasSeenGettingStarted === true}
|
||||
|
||||
title={
|
||||
<React.Fragment>
|
||||
<Typography color="inherit" sx={{
|
||||
fontSize: '16px'
|
||||
}}>Use this option to connect additional Qortal wallets you've already made, in order to login with them afterwards. You will need access to your backup JSON file in order to do so.</Typography>
|
||||
</React.Fragment>
|
||||
}
|
||||
>
|
||||
<CustomButton sx={{
|
||||
padding: '10px'
|
||||
}} {...getRootProps()}>
|
||||
<input {...getInputProps()} />
|
||||
Add account
|
||||
</CustomButton>
|
||||
disableHoverListener={hasSeenGettingStarted === true}
|
||||
title={
|
||||
<React.Fragment>
|
||||
<Typography
|
||||
color="inherit"
|
||||
sx={{
|
||||
fontSize: '16px',
|
||||
}}
|
||||
>
|
||||
Already have a Qortal account? Enter your secret backup phrase
|
||||
here to access it. This phrase is one of the ways to recover
|
||||
your account.
|
||||
</Typography>
|
||||
</React.Fragment>
|
||||
}
|
||||
>
|
||||
<CustomButton
|
||||
onClick={handleSetSeedValue}
|
||||
sx={{
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
Add seed-phrase
|
||||
</CustomButton>
|
||||
</HtmlTooltip>
|
||||
|
||||
<HtmlTooltip
|
||||
disableHoverListener={hasSeenGettingStarted === true}
|
||||
title={
|
||||
<React.Fragment>
|
||||
<Typography
|
||||
color="inherit"
|
||||
sx={{
|
||||
fontSize: '16px',
|
||||
}}
|
||||
>
|
||||
Use this option to connect additional Qortal wallets you've
|
||||
already made, in order to login with them afterwards. You will
|
||||
need access to your backup JSON file in order to do so.
|
||||
</Typography>
|
||||
</React.Fragment>
|
||||
}
|
||||
>
|
||||
<CustomButton
|
||||
sx={{
|
||||
padding: '10px',
|
||||
}}
|
||||
{...getRootProps()}
|
||||
>
|
||||
<input {...getInputProps()} />
|
||||
Add account
|
||||
</CustomButton>
|
||||
</HtmlTooltip>
|
||||
</Box>
|
||||
|
||||
<Dialog
|
||||
open={isOpenSeedModal}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && seedValue && seedName && password) {
|
||||
onOk({seedValue, seedName, password});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
Type or paste in your seed-phrase
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
|
||||
}}
|
||||
>
|
||||
<Dialog
|
||||
open={isOpenSeedModal}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && seedValue && seedName && password) {
|
||||
onOk({ seedValue, seedName, password });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
Type or paste in your seed-phrase
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<Label>Name</Label>
|
||||
<Input
|
||||
placeholder="Name"
|
||||
value={seedName}
|
||||
onChange={(e) => setSeedName(e.target.value)}
|
||||
/>
|
||||
<Spacer height="7px" />
|
||||
<Label>Seed-phrase</Label>
|
||||
<PasswordField
|
||||
placeholder="Seed-phrase"
|
||||
<Input
|
||||
placeholder="Name"
|
||||
value={seedName}
|
||||
onChange={(e) => setSeedName(e.target.value)}
|
||||
/>
|
||||
|
||||
<Spacer height="7px" />
|
||||
|
||||
<Label>Seed-phrase</Label>
|
||||
<PasswordField
|
||||
placeholder="Seed-phrase"
|
||||
id="standard-adornment-password"
|
||||
value={seedValue}
|
||||
onChange={(e) => setSeedValue(e.target.value)}
|
||||
autoComplete="off"
|
||||
sx={{
|
||||
width: '100%'
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
<Spacer height="7px" />
|
||||
|
||||
<Label>Choose new password</Label>
|
||||
<PasswordField
|
||||
<Spacer height="7px" />
|
||||
|
||||
<Label>Choose new password</Label>
|
||||
<PasswordField
|
||||
id="standard-adornment-password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
autoComplete="off"
|
||||
sx={{
|
||||
width: '100%'
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
|
||||
</Box>
|
||||
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button disabled={isLoadingEncryptSeed} variant="contained" onClick={()=> {
|
||||
setIsOpenSeedModal(false)
|
||||
setSeedValue('')
|
||||
setSeedName('')
|
||||
setPassword('')
|
||||
setSeedError('')
|
||||
}}>
|
||||
Close
|
||||
</Button>
|
||||
<LoadingButton
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
disabled={isLoadingEncryptSeed}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
setIsOpenSeedModal(false);
|
||||
setSeedValue('');
|
||||
setSeedName('');
|
||||
setPassword('');
|
||||
setSeedError('');
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
<LoadingButton
|
||||
loading={isLoadingEncryptSeed}
|
||||
disabled={!seedValue || !seedName || !password}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
if(!seedValue || !seedName || !password) return
|
||||
onOk({seedValue, seedName, password});
|
||||
}}
|
||||
autoFocus
|
||||
>
|
||||
Add
|
||||
</LoadingButton>
|
||||
<Typography sx={{
|
||||
fontSize: '14px',
|
||||
visibility: seedError ? 'visible' : 'hidden'
|
||||
}}>{seedError}</Typography>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
disabled={!seedValue || !seedName || !password}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
if (!seedValue || !seedName || !password) return;
|
||||
onOk({ seedValue, seedName, password });
|
||||
}}
|
||||
autoFocus
|
||||
>
|
||||
Add
|
||||
</LoadingButton>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '14px',
|
||||
visibility: seedError ? 'visible' : 'hidden',
|
||||
}}
|
||||
>
|
||||
{seedError}
|
||||
</Typography>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</div>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
||||
const [name, setName] = useState("");
|
||||
const [note, setNote] = useState("");
|
||||
const [name, setName] = useState('');
|
||||
const [note, setNote] = useState('');
|
||||
const [isEdit, setIsEdit] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@ -382,71 +418,81 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
||||
}}
|
||||
sx={{
|
||||
width: '100%',
|
||||
padding: '10px'
|
||||
padding: '10px',
|
||||
}}
|
||||
|
||||
>
|
||||
<ListItem
|
||||
sx={{
|
||||
bgcolor: "background.paper",
|
||||
bgcolor: 'background.paper', // TODO: set background color
|
||||
flexGrow: 1,
|
||||
"&:hover": { backgroundColor: "secondary.main", transform: "scale(1.01)" },
|
||||
transition: "all 0.1s ease-in-out",
|
||||
'&:hover': {
|
||||
backgroundColor: 'secondary.main',
|
||||
transform: 'scale(1.01)',
|
||||
},
|
||||
transition: 'all 0.1s ease-in-out',
|
||||
}}
|
||||
|
||||
alignItems="flex-start"
|
||||
>
|
||||
<ListItemAvatar>
|
||||
<Avatar alt="" src="/static/images/avatar/1.jpg" />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
|
||||
primary={wallet?.name ? wallet.name : wallet?.filename ? parsefilenameQortal(wallet?.filename) : "No name"}
|
||||
primary={
|
||||
wallet?.name
|
||||
? wallet.name
|
||||
: wallet?.filename
|
||||
? parsefilenameQortal(wallet?.filename)
|
||||
: 'No name'
|
||||
}
|
||||
secondary={
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
component="span"
|
||||
variant="body2"
|
||||
sx={{ color: "text.primary", display: "inline" }}
|
||||
sx={{ color: 'text.primary', display: 'inline' }}
|
||||
>
|
||||
{wallet?.address0}
|
||||
</Typography>
|
||||
{wallet?.note}
|
||||
<Typography sx={{
|
||||
textAlign: 'end',
|
||||
marginTop: '5px'
|
||||
}}>Login</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
textAlign: 'end',
|
||||
marginTop: '5px',
|
||||
}}
|
||||
>
|
||||
Login
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
<IconButton
|
||||
sx={{
|
||||
alignSelf: 'flex-start'
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsEdit(true);
|
||||
}}
|
||||
edge="end"
|
||||
aria-label="edit"
|
||||
>
|
||||
<EditIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
sx={{
|
||||
alignSelf: 'flex-start',
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsEdit(true);
|
||||
}}
|
||||
edge="end"
|
||||
aria-label="edit"
|
||||
>
|
||||
<EditIcon
|
||||
sx={{
|
||||
color: 'white',
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
</ButtonBase>
|
||||
{isEdit && (
|
||||
<Box
|
||||
sx={{
|
||||
padding: "8px",
|
||||
padding: '8px',
|
||||
}}
|
||||
>
|
||||
<Label>Name</Label>
|
||||
@ -455,10 +501,12 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
sx={{
|
||||
width: "100%",
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Spacer height="10px" />
|
||||
|
||||
<Label>Note</Label>
|
||||
<Input
|
||||
placeholder="Note"
|
||||
@ -468,48 +516,54 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
||||
maxLength: 100,
|
||||
}}
|
||||
sx={{
|
||||
width: "100%",
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Spacer height="10px" />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
justifyContent: "flex-end",
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
justifyContent: 'flex-end',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Button size="small" variant="contained" onClick={() => setIsEdit(false)}>
|
||||
<Button
|
||||
size="small"
|
||||
variant="contained"
|
||||
onClick={() => setIsEdit(false)}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
sx={{
|
||||
backgroundColor: 'var(--danger)',
|
||||
"&:hover": {
|
||||
backgroundColor: "var(--danger)",
|
||||
},
|
||||
"&:focus": {
|
||||
backgroundColor: "var(--danger)",
|
||||
},
|
||||
}}
|
||||
size="small"
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--danger)',
|
||||
},
|
||||
'&:focus': {
|
||||
backgroundColor: 'var(--danger)',
|
||||
},
|
||||
}}
|
||||
size="small"
|
||||
variant="contained"
|
||||
onClick={() => updateWalletItem(idx, null)}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
backgroundColor: "#5EB049",
|
||||
"&:hover": {
|
||||
backgroundColor: "#5EB049",
|
||||
},
|
||||
"&:focus": {
|
||||
backgroundColor: "#5EB049",
|
||||
},
|
||||
}}
|
||||
size="small"
|
||||
sx={{
|
||||
backgroundColor: '#5EB049',
|
||||
'&:hover': {
|
||||
backgroundColor: '#5EB049',
|
||||
},
|
||||
'&:focus': {
|
||||
backgroundColor: '#5EB049',
|
||||
},
|
||||
}}
|
||||
size="small"
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
updateWalletItem(idx, {
|
||||
@ -525,9 +579,6 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => {
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,8 +1,18 @@
|
||||
import React from "react";
|
||||
import { useTheme } from '@mui/material';
|
||||
import { SVGProps } from '../svgs/interfaces';
|
||||
|
||||
export const WalletIcon: React.FC<SVGProps> = ({
|
||||
color,
|
||||
width,
|
||||
...children
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const setColor = color ? color : theme.palette.text.primary;
|
||||
|
||||
export const WalletIcon = ({ color, height, width }) => {
|
||||
return (
|
||||
<svg
|
||||
{...children}
|
||||
width={width || 30}
|
||||
height={width || 30}
|
||||
viewBox="0 0 31 31"
|
||||
@ -11,13 +21,13 @@ export const WalletIcon = ({ color, height, width }) => {
|
||||
>
|
||||
<path
|
||||
d="M19.0118 22.0891C18.0124 22.8671 16.6997 23.3391 15.2618 23.3391C13.8241 23.3391 12.5113 22.8671 11.5118 22.0891"
|
||||
stroke={color}
|
||||
stroke={setColor}
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M3.20108 17.356C2.7598 14.4844 2.53917 13.0486 3.08205 11.7758C3.62493 10.503 4.82938 9.63215 7.23827 7.89044L9.03808 6.58911C12.0347 4.42245 13.5331 3.33911 15.2618 3.33911C16.9907 3.33911 18.4889 4.42245 21.4856 6.58911L23.2854 7.89044C25.6943 9.63215 26.8988 10.503 27.4417 11.7758C27.9846 13.0486 27.7639 14.4844 27.3226 17.356L26.9463 19.8046C26.3208 23.8752 26.0079 25.9106 24.5481 27.1249C23.0882 28.3391 20.9539 28.3391 16.6853 28.3391H13.8383C9.56977 28.3391 7.43548 28.3391 5.97559 27.1249C4.5157 25.9106 4.20293 23.8752 3.57738 19.8046L3.20108 17.356Z"
|
||||
stroke={color}
|
||||
stroke={setColor}
|
||||
strokeWidth="2"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React from "react";
|
||||
import { styled } from "@mui/system";
|
||||
import { SVGProps } from "./interfaces";
|
||||
import React from 'react';
|
||||
import { styled } from '@mui/system';
|
||||
import { SVGProps } from './interfaces';
|
||||
|
||||
// Create a styled container with hover effects
|
||||
const SvgContainer = styled("svg")({
|
||||
"& path": {
|
||||
fill: "rgba(41, 41, 43, 1)", // Default to red if no color prop
|
||||
const SvgContainer = styled('svg')({
|
||||
'& path': {
|
||||
fill: 'rgba(41, 41, 43, 1)', // Default to red if no color prop
|
||||
},
|
||||
});
|
||||
|
||||
@ -20,7 +20,7 @@ export const CreateThreadIcon: React.FC<SVGProps> = ({ color, opacity }) => {
|
||||
>
|
||||
<path
|
||||
d="M0 9.80209V9.0205C0.0460138 8.67679 0.080024 8.31425 0.144043 7.98466C0.469856 6.30568 1.25577 4.79934 2.38071 3.6977C4.13924 1.88262 6.22987 0.985679 8.52256 0.674927C9.9086 0.485649 11.3116 0.565177 12.6758 0.910345C14.5124 1.34351 16.1889 2.2075 17.6053 3.67886C18.7276 4.84183 19.5319 6.24257 19.858 7.98466C19.918 8.31189 19.952 8.64383 20 8.97577V9.80209C19.9827 9.8676 19.9693 9.93447 19.96 10.0022C19.8708 11.2186 19.5113 12.3861 18.9177 13.3875C17.961 15.0025 16.6297 16.2594 15.0825 17.0082C12.4657 18.3525 9.75693 18.5667 6.98209 17.8346C6.8589 17.8074 6.73157 17.8264 6.61799 17.8887C5.15955 18.7339 3.70511 19.5908 2.24867 20.4501C2.18866 20.4854 2.12464 20.5183 2.0146 20.5748L3.78714 16.3703C3.37301 16.0148 2.96889 15.7017 2.60078 15.3415C1.42243 14.1879 0.556167 12.7895 0.182055 11.0192C0.0980294 10.6213 0.060018 10.2094 0 9.80209ZM14.0042 10.5931C14.1362 10.5968 14.2676 10.5698 14.3907 10.5135C14.5138 10.4572 14.6262 10.3728 14.7214 10.2651C14.8167 10.1574 14.8928 10.0286 14.9455 9.8861C14.9982 9.7436 15.0264 9.59023 15.0285 9.43484V9.4113C15.0285 9.25517 15.0024 9.10058 14.9516 8.95634C14.9008 8.8121 14.8264 8.68104 14.7326 8.57064C14.6388 8.46025 14.5274 8.37268 14.4048 8.31293C14.2823 8.25319 14.1509 8.22243 14.0182 8.22243C13.8855 8.22243 13.7542 8.25319 13.6316 8.31293C13.509 8.37268 13.3976 8.46025 13.3038 8.57064C13.21 8.68104 13.1356 8.8121 13.0848 8.95634C13.034 9.10058 13.0079 9.25517 13.0079 9.4113C13.0074 9.56588 13.0327 9.71906 13.0825 9.86211C13.1323 10.0052 13.2055 10.1353 13.2981 10.245C13.3906 10.3547 13.5005 10.442 13.6217 10.5017C13.7429 10.5614 13.8728 10.5925 14.0042 10.5931ZM10.003 10.5931C10.203 10.5926 10.3983 10.5225 10.5644 10.3915C10.7306 10.2606 10.86 10.0746 10.9364 9.85719C11.0129 9.63976 11.0329 9.40056 10.9939 9.16977C10.9549 8.93898 10.8588 8.72694 10.7175 8.5604C10.5763 8.39385 10.3962 8.28026 10.2002 8.23396C10.0041 8.18765 9.80084 8.21071 9.61591 8.30022C9.43099 8.38973 9.27273 8.54168 9.1611 8.7369C9.04948 8.93212 8.98949 9.16187 8.9887 9.39717C8.98975 9.71356 9.09688 10.0167 9.28682 10.2406C9.47675 10.4646 9.73413 10.5912 10.003 10.5931ZM4.98349 9.3854C4.9836 9.61979 5.04316 9.8488 5.15456 10.0431C5.26595 10.2374 5.42411 10.3882 5.60876 10.476C5.79341 10.5639 5.99616 10.5849 6.19102 10.5364C6.38588 10.4878 6.56399 10.3719 6.70252 10.2035C6.84105 10.0351 6.93371 9.82183 6.96861 9.59108C7.00352 9.36032 6.97909 9.12255 6.89845 8.90823C6.8178 8.69392 6.68463 8.51281 6.51597 8.38811C6.34732 8.26342 6.15087 8.20081 5.95179 8.20831C5.69208 8.21809 5.44579 8.34641 5.26507 8.56611C5.08434 8.78581 4.98336 9.07963 4.98349 9.3854Z"
|
||||
fill="#29292B"
|
||||
fill={color}
|
||||
/>
|
||||
</SvgContainer>
|
||||
);
|
||||
|
32
src/assets/svgs/Download.tsx
Normal file
32
src/assets/svgs/Download.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import { useTheme } from '@mui/material';
|
||||
import { SVGProps } from './interfaces';
|
||||
|
||||
export const Download: React.FC<SVGProps> = ({
|
||||
color,
|
||||
opacity,
|
||||
...children
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const setColor = color ? color : theme.palette.text.primary;
|
||||
const setOpacity = opacity ? opacity : 1;
|
||||
|
||||
return (
|
||||
<svg
|
||||
{...children}
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill={setColor}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12.8047 0.393196V7.21185H16.3036L10.0003 13.5139L3.69697 7.21185H7.19584V0H12.8045L12.8047 0.393196ZM2.7047 16.8587V13.9861H0V18.6179C0 19.3774 0.622589 20 1.38213 20H18.6179C19.3774 20 20 19.3774 20 18.6179V13.9861H17.2962V17.2963L2.70461 17.2954L2.7047 16.8587Z"
|
||||
fill={setColor}
|
||||
fill-opacity={setOpacity}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
28
src/assets/svgs/Logout.tsx
Normal file
28
src/assets/svgs/Logout.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import { useTheme } from '@mui/material';
|
||||
import { SVGProps } from './interfaces';
|
||||
|
||||
export const Logout: React.FC<SVGProps> = ({ color, opacity, ...children }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const setColor = color ? color : theme.palette.text.primary;
|
||||
const setOpacity = opacity ? opacity : 1;
|
||||
|
||||
return (
|
||||
<svg
|
||||
{...children}
|
||||
width="18"
|
||||
height="20"
|
||||
viewBox="0 0 18 20"
|
||||
fill={setColor}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M7.56485 0H16.3611C17.2662 0 18 0.727797 18 1.62558V18.3744C18 19.2722 17.2662 20 16.3611 20H7.56485C6.65969 20 5.92593 19.2722 5.92593 18.3744V12.6013H10.6168C11.4569 12.6013 12.1404 11.9039 12.1404 11.0467V8.87329C12.1404 8.01613 11.4569 7.31875 10.6168 7.31875H5.92593V1.62558C5.92593 0.727797 6.65969 0 7.56485 0ZM11.1667 11.0467C11.1667 11.3719 10.9205 11.6354 10.6168 11.6354H4.8144C4.74521 11.6354 4.68911 11.6955 4.68911 11.7696V12.8632C4.68911 13.3492 4.17007 13.6259 3.8078 13.3329L0.218431 10.4298C-0.0728102 10.1942 -0.0728102 9.72579 0.218431 9.49024L3.8078 6.58709C4.17005 6.29409 4.68911 6.57077 4.68911 7.05684V8.1504C4.68911 8.2245 4.74521 8.28454 4.8144 8.28454H10.6168C10.9205 8.28454 11.1667 8.54813 11.1667 8.87329V11.0467Z"
|
||||
fill={setColor}
|
||||
fill-opacity={setOpacity}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
27
src/assets/svgs/Return.tsx
Normal file
27
src/assets/svgs/Return.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { useTheme } from '@mui/material';
|
||||
import { SVGProps } from './interfaces';
|
||||
|
||||
export const Return: React.FC<SVGProps> = ({ color, opacity, ...children }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const setColor = color ? color : theme.palette.text.primary;
|
||||
|
||||
return (
|
||||
<svg
|
||||
{...children}
|
||||
width="20"
|
||||
height="16"
|
||||
viewBox="0 0 20 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M2.645 5.81803H15C15.9471 5.81803 16.8557 6.20131 17.5257 6.88278C18.195 7.56497 18.5714 8.49007 18.5714 9.45445V10.909C18.5714 11.8734 18.195 12.7985 17.5257 13.4807C16.8557 14.1622 15.9471 14.5454 15 14.5454C12.0164 14.5454 8.57143 14.5454 8.57143 14.5454C8.17714 14.5454 7.85714 14.8713 7.85714 15.2727C7.85714 15.6742 8.17714 16 8.57143 16H15C16.3264 16 17.5979 15.464 18.5357 14.5091C19.4736 13.5541 20 12.2596 20 10.909C20 10.4268 20 9.93664 20 9.45445C20 8.10461 19.4736 6.80932 18.5357 5.8544C17.5979 4.9002 16.3264 4.36347 15 4.36347H2.645L6.17929 1.27906C6.47857 1.01797 6.51286 0.55832 6.25643 0.253588C6 -0.0511433 5.54857 -0.0860541 5.24929 0.175041L0.249285 4.53874C0.0914279 4.67692 0 4.87838 0 5.09075C0 5.30312 0.0914279 5.50458 0.249285 5.64276L5.24929 10.0065C5.54857 10.2676 6 10.2326 6.25643 9.92791C6.51286 9.62318 6.47857 9.16353 6.17929 8.90244L2.645 5.81803Z"
|
||||
fill={setColor}
|
||||
fill-opacity={opacity}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
@ -1,12 +1,14 @@
|
||||
import { useTheme } from "@mui/material";
|
||||
import { useTheme } from '@mui/material';
|
||||
import { SVGProps } from './interfaces';
|
||||
|
||||
export const SaveIcon = ({ color }) => {
|
||||
export const SaveIcon: React.FC<SVGProps> = ({ color, ...children }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const setColor = color ? color : theme.palette.text.primary
|
||||
const setColor = color ? color : theme.palette.text.primary;
|
||||
|
||||
return (
|
||||
<svg
|
||||
{...children}
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
|
@ -6,14 +6,23 @@ import { SVGProps } from './interfaces';
|
||||
const SvgContainer = styled('svg')({
|
||||
'& path': {
|
||||
fill: 'rgba(41, 41, 43, 1)', // Default to red if no color prop
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const SendNewMessage:React.FC<SVGProps> = ({ color, opacity }) => {
|
||||
export const SendNewMessage: React.FC<SVGProps> = ({ color, opacity }) => {
|
||||
return (
|
||||
<SvgContainer width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fillRule="evenodd" clipRule="evenodd" d="M3.33271 10.2306C2.88006 10.001 2.89088 9.65814 3.3554 9.46527L16.3563 4.06742C16.8214 3.87427 17.0961 4.11004 16.9689 4.59692L14.1253 15.4847C13.9985 15.9703 13.5515 16.1438 13.1241 15.8705L10.0773 13.9219C9.8629 13.7848 9.56272 13.8345 9.40985 14.0292L8.41215 15.2997C8.10197 15.6946 7.71724 15.6311 7.5525 15.1567L6.67584 12.6326C6.51125 12.1587 6.01424 11.5902 5.55821 11.359L3.33271 10.2306Z" />
|
||||
<SvgContainer
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M3.33271 10.2306C2.88006 10.001 2.89088 9.65814 3.3554 9.46527L16.3563 4.06742C16.8214 3.87427 17.0961 4.11004 16.9689 4.59692L14.1253 15.4847C13.9985 15.9703 13.5515 16.1438 13.1241 15.8705L10.0773 13.9219C9.8629 13.7848 9.56272 13.8345 9.40985 14.0292L8.41215 15.2997C8.10197 15.6946 7.71724 15.6311 7.5525 15.1567L6.67584 12.6326C6.51125 12.1587 6.01424 11.5902 5.55821 11.359L3.33271 10.2306Z"
|
||||
/>
|
||||
</SvgContainer>
|
||||
|
||||
);
|
||||
};
|
||||
|
@ -1,16 +1,16 @@
|
||||
|
||||
|
||||
|
||||
import React from 'react';
|
||||
|
||||
|
||||
export const StarEmptyIcon = () => {
|
||||
return (
|
||||
<svg width="12" height="11" viewBox="0 0 12 11" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.2726 0.162533L7.89126 3.31595C7.9357 3.40243 8.02078 3.46234 8.11994 3.47588L11.7399 3.98173C11.8542 3.99736 11.9496 4.07446 11.9853 4.18022C12.0206 4.28598 11.9913 4.40215 11.9084 4.47977L9.28882 6.93449V6.93397C9.21729 7.00117 9.18478 7.09807 9.20157 7.19288L9.81988 10.6588C9.83939 10.7682 9.79278 10.8786 9.69903 10.9443C9.60529 11.0094 9.48119 11.0182 9.37931 10.9667L6.14144 9.32987C6.05311 9.28559 5.9469 9.28559 5.85856 9.32987L2.62069 10.9667C2.51881 11.0182 2.39472 11.0094 2.30096 10.9443C2.20722 10.8786 2.16062 10.7682 2.18012 10.6588L2.79842 7.19288C2.81522 7.09807 2.78271 7.00117 2.71118 6.93397L0.0916083 4.47978C0.0086971 4.40216 -0.0205644 4.28599 0.0146582 4.18023C0.0504232 4.07448 0.145798 3.99738 0.260135 3.98175L3.88006 3.47589C3.97923 3.46235 4.0643 3.40244 4.10874 3.31596L5.7274 0.162545C5.77888 0.0630431 5.88455 0 5.99997 0C6.11539 0 6.22113 0.0630238 6.2726 0.162533Z" fill="#727376"/>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
<svg
|
||||
width="12"
|
||||
height="11"
|
||||
viewBox="0 0 12 11"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.2726 0.162533L7.89126 3.31595C7.9357 3.40243 8.02078 3.46234 8.11994 3.47588L11.7399 3.98173C11.8542 3.99736 11.9496 4.07446 11.9853 4.18022C12.0206 4.28598 11.9913 4.40215 11.9084 4.47977L9.28882 6.93449V6.93397C9.21729 7.00117 9.18478 7.09807 9.20157 7.19288L9.81988 10.6588C9.83939 10.7682 9.79278 10.8786 9.69903 10.9443C9.60529 11.0094 9.48119 11.0182 9.37931 10.9667L6.14144 9.32987C6.05311 9.28559 5.9469 9.28559 5.85856 9.32987L2.62069 10.9667C2.51881 11.0182 2.39472 11.0094 2.30096 10.9443C2.20722 10.8786 2.16062 10.7682 2.18012 10.6588L2.79842 7.19288C2.81522 7.09807 2.78271 7.00117 2.71118 6.93397L0.0916083 4.47978C0.0086971 4.40216 -0.0205644 4.28599 0.0146582 4.18023C0.0504232 4.07448 0.145798 3.99738 0.260135 3.98175L3.88006 3.47589C3.97923 3.46235 4.0643 3.40244 4.10874 3.31596L5.7274 0.162545C5.77888 0.0630431 5.88455 0 5.99997 0C6.11539 0 6.22113 0.0630238 6.2726 0.162533Z"
|
||||
fill="#727376"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,3 @@
|
||||
import React from "react";
|
||||
|
||||
export const StarFilledIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
|
@ -1,6 +1,8 @@
|
||||
export interface SVGProps {
|
||||
color: string
|
||||
height: string
|
||||
width: string
|
||||
opacity?: number
|
||||
import React from 'react';
|
||||
|
||||
export interface SVGProps extends React.SVGProps<SVGSVGElement> {
|
||||
color?: string;
|
||||
height?: string;
|
||||
opacity?: number;
|
||||
width?: string;
|
||||
}
|
||||
|
@ -1,180 +1,195 @@
|
||||
import { atom, selectorFamily } from 'recoil';
|
||||
|
||||
|
||||
export const sortablePinnedAppsAtom = atom({
|
||||
key: 'sortablePinnedAppsFromAtom',
|
||||
default: [{
|
||||
name: 'Q-Tube',
|
||||
service: 'APP'
|
||||
}, {
|
||||
name: 'Q-Mail',
|
||||
service: 'APP'
|
||||
}, {
|
||||
name: 'Q-Share',
|
||||
service: 'APP'
|
||||
}, {
|
||||
name: 'Q-Fund',
|
||||
service: 'APP'
|
||||
}, {
|
||||
name: 'Q-Shop',
|
||||
service: 'APP'
|
||||
},
|
||||
{
|
||||
name: 'Q-Trade',
|
||||
service: 'APP'
|
||||
},
|
||||
{
|
||||
name: 'Q-Support',
|
||||
service: 'APP'
|
||||
},
|
||||
{
|
||||
name: 'Q-Manager',
|
||||
service: 'APP'
|
||||
},
|
||||
{
|
||||
name: 'Q-Blog',
|
||||
service: 'APP'
|
||||
},
|
||||
{
|
||||
name: 'Q-Mintership',
|
||||
service: 'APP'
|
||||
},
|
||||
{
|
||||
name: 'Q-Wallets',
|
||||
service: 'APP'
|
||||
},
|
||||
{
|
||||
name: 'Q-Search',
|
||||
service: 'APP'
|
||||
},
|
||||
],
|
||||
key: 'sortablePinnedAppsFromAtom',
|
||||
default: [
|
||||
{
|
||||
name: 'Q-Tube',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Mail',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Share',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Fund',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Shop',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Trade',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Support',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Manager',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Blog',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Mintership',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Wallets',
|
||||
service: 'APP',
|
||||
},
|
||||
{
|
||||
name: 'Q-Search',
|
||||
service: 'APP',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export const canSaveSettingToQdnAtom = atom({
|
||||
key: 'canSaveSettingToQdnAtom',
|
||||
default: false,
|
||||
key: 'canSaveSettingToQdnAtom',
|
||||
default: false,
|
||||
});
|
||||
|
||||
export const settingsQDNLastUpdatedAtom = atom({
|
||||
key: 'settingsQDNLastUpdatedAtom',
|
||||
default: -100,
|
||||
key: 'settingsQDNLastUpdatedAtom',
|
||||
default: -100,
|
||||
});
|
||||
|
||||
export const settingsLocalLastUpdatedAtom = atom({
|
||||
key: 'settingsLocalLastUpdatedAtom',
|
||||
default: 0,
|
||||
key: 'settingsLocalLastUpdatedAtom',
|
||||
default: 0,
|
||||
});
|
||||
|
||||
export const oldPinnedAppsAtom = atom({
|
||||
key: 'oldPinnedAppsAtom',
|
||||
default: [],
|
||||
});
|
||||
export const isUsingImportExportSettingsAtom = atom({
|
||||
key: 'isUsingImportExportSettingsAtom',
|
||||
default: null,
|
||||
key: 'oldPinnedAppsAtom',
|
||||
default: [],
|
||||
});
|
||||
|
||||
export const isUsingImportExportSettingsAtom = atom({
|
||||
key: 'isUsingImportExportSettingsAtom',
|
||||
default: null,
|
||||
});
|
||||
|
||||
export const fullScreenAtom = atom({
|
||||
key: 'fullScreenAtom',
|
||||
default: false,
|
||||
key: 'fullScreenAtom',
|
||||
default: false,
|
||||
});
|
||||
|
||||
export const hasSettingsChangedAtom = atom({
|
||||
key: 'hasSettingsChangedAtom',
|
||||
default: false,
|
||||
key: 'hasSettingsChangedAtom',
|
||||
default: false,
|
||||
});
|
||||
|
||||
export const navigationControllerAtom = atom({
|
||||
key: 'navigationControllerAtom',
|
||||
default: {},
|
||||
key: 'navigationControllerAtom',
|
||||
default: {},
|
||||
});
|
||||
|
||||
export const enabledDevModeAtom = atom({
|
||||
key: 'enabledDevModeAtom',
|
||||
default: false,
|
||||
key: 'enabledDevModeAtom',
|
||||
default: false,
|
||||
});
|
||||
|
||||
export const myGroupsWhereIAmAdminAtom = atom({
|
||||
key: 'myGroupsWhereIAmAdminAtom',
|
||||
default: [],
|
||||
key: 'myGroupsWhereIAmAdminAtom',
|
||||
default: [],
|
||||
});
|
||||
|
||||
export const promotionTimeIntervalAtom = atom({
|
||||
key: 'promotionTimeIntervalAtom',
|
||||
default: 0,
|
||||
key: 'promotionTimeIntervalAtom',
|
||||
default: 0,
|
||||
});
|
||||
|
||||
export const promotionsAtom = atom({
|
||||
key: 'promotionsAtom',
|
||||
default: [],
|
||||
key: 'promotionsAtom',
|
||||
default: [],
|
||||
});
|
||||
|
||||
export const resourceDownloadControllerAtom = atom({
|
||||
key: 'resourceDownloadControllerAtom',
|
||||
default: {},
|
||||
key: 'resourceDownloadControllerAtom',
|
||||
default: {},
|
||||
});
|
||||
|
||||
export const resourceKeySelector = selectorFamily({
|
||||
key: 'resourceKeySelector',
|
||||
get: (key) => ({ get }) => {
|
||||
const resources = get(resourceDownloadControllerAtom);
|
||||
return resources[key] || null; // Return the value for the key or null if not found
|
||||
},
|
||||
get:
|
||||
(key) =>
|
||||
({ get }) => {
|
||||
const resources = get(resourceDownloadControllerAtom);
|
||||
return resources[key] || null; // Return the value for the key or null if not found
|
||||
},
|
||||
});
|
||||
|
||||
export const blobControllerAtom = atom({
|
||||
key: 'blobControllerAtom',
|
||||
default: {},
|
||||
key: 'blobControllerAtom',
|
||||
default: {},
|
||||
});
|
||||
|
||||
export const blobKeySelector = selectorFamily({
|
||||
key: 'blobKeySelector',
|
||||
get: (key) => ({ get }) => {
|
||||
const blobs = get(blobControllerAtom);
|
||||
return blobs[key] || null; // Return the value for the key or null if not found
|
||||
},
|
||||
get:
|
||||
(key) =>
|
||||
({ get }) => {
|
||||
const blobs = get(blobControllerAtom);
|
||||
return blobs[key] || null; // Return the value for the key or null if not found
|
||||
},
|
||||
});
|
||||
|
||||
export const selectedGroupIdAtom = atom({
|
||||
key: 'selectedGroupIdAtom',
|
||||
default: null,
|
||||
key: 'selectedGroupIdAtom',
|
||||
default: null,
|
||||
});
|
||||
|
||||
export const addressInfoControllerAtom = atom({
|
||||
key: 'addressInfoControllerAtom',
|
||||
default: {},
|
||||
key: 'addressInfoControllerAtom',
|
||||
default: {},
|
||||
});
|
||||
|
||||
export const addressInfoKeySelector = selectorFamily({
|
||||
key: 'addressInfoKeySelector',
|
||||
get: (key) => ({ get }) => {
|
||||
const userInfo = get(addressInfoControllerAtom);
|
||||
return userInfo[key] || null; // Return the value for the key or null if not found
|
||||
},
|
||||
get:
|
||||
(key) =>
|
||||
({ get }) => {
|
||||
const userInfo = get(addressInfoControllerAtom);
|
||||
return userInfo[key] || null; // Return the value for the key or null if not found
|
||||
},
|
||||
});
|
||||
|
||||
export const isDisabledEditorEnterAtom = atom({
|
||||
key: 'isDisabledEditorEnterAtom',
|
||||
default: false,
|
||||
key: 'isDisabledEditorEnterAtom',
|
||||
default: false,
|
||||
});
|
||||
|
||||
export const qMailLastEnteredTimestampAtom = atom({
|
||||
key: 'qMailLastEnteredTimestampAtom',
|
||||
default: null,
|
||||
key: 'qMailLastEnteredTimestampAtom',
|
||||
default: null,
|
||||
});
|
||||
|
||||
export const lastPaymentSeenTimestampAtom = atom<null | number>({
|
||||
key: 'lastPaymentSeenTimestampAtom',
|
||||
default: null,
|
||||
key: 'lastPaymentSeenTimestampAtom',
|
||||
default: null,
|
||||
});
|
||||
|
||||
export const mailsAtom = atom({
|
||||
key: 'mailsAtom',
|
||||
default: [],
|
||||
key: 'mailsAtom',
|
||||
default: [],
|
||||
});
|
||||
|
||||
export const groupsPropertiesAtom = atom({
|
||||
key: 'groupsPropertiesAtom',
|
||||
default: {},
|
||||
});
|
||||
key: 'groupsPropertiesAtom',
|
||||
default: {},
|
||||
});
|
||||
|
||||
export const isOpenBlockedModalAtom = atom({
|
||||
key: 'isOpenBlockedModalAtom',
|
||||
default: false,
|
||||
});
|
||||
|
@ -3,15 +3,15 @@ import {
|
||||
InputAdornment,
|
||||
TextField,
|
||||
TextFieldProps,
|
||||
} from "@mui/material";
|
||||
import React, { useRef, useState } from "react";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import RemoveIcon from "@mui/icons-material/Remove";
|
||||
} from '@mui/material';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import RemoveIcon from '@mui/icons-material/Remove';
|
||||
import {
|
||||
removeTrailingZeros,
|
||||
setNumberWithinBounds,
|
||||
} from "./numberFunctions.ts";
|
||||
import { CustomInput } from "../App-styles.ts";
|
||||
} from './numberFunctions.ts';
|
||||
import { CustomInput } from '../styles/App-styles.ts';
|
||||
|
||||
type eventType = React.ChangeEvent<HTMLInputElement>;
|
||||
type BoundedNumericTextFieldProps = {
|
||||
@ -37,18 +37,18 @@ export const BoundedNumericTextField = ({
|
||||
...props
|
||||
}: BoundedNumericTextFieldProps) => {
|
||||
const [textFieldValue, setTextFieldValue] = useState<string>(
|
||||
initialValue || ""
|
||||
initialValue || ''
|
||||
);
|
||||
const ref = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const stringIsEmpty = (value: string) => {
|
||||
return value === "";
|
||||
return value === '';
|
||||
};
|
||||
const isAllZerosNum = /^0*\.?0*$/;
|
||||
const isFloatNum = /^-?[0-9]*\.?[0-9]*$/;
|
||||
const isIntegerNum = /^-?[0-9]+$/;
|
||||
const skipMinMaxCheck = (value: string) => {
|
||||
const lastIndexIsDecimal = value.charAt(value.length - 1) === ".";
|
||||
const lastIndexIsDecimal = value.charAt(value.length - 1) === '.';
|
||||
const isEmpty = stringIsEmpty(value);
|
||||
const isAllZeros = isAllZerosNum.test(value);
|
||||
const isInteger = isIntegerNum.test(value);
|
||||
@ -69,7 +69,7 @@ export const BoundedNumericTextField = ({
|
||||
|
||||
const getSigDigits = (number: string) => {
|
||||
if (isIntegerNum.test(number)) return 0;
|
||||
const decimalSplit = number.split(".");
|
||||
const decimalSplit = number.split('.');
|
||||
return decimalSplit[decimalSplit.length - 1].length;
|
||||
};
|
||||
|
||||
@ -78,15 +78,15 @@ export const BoundedNumericTextField = ({
|
||||
};
|
||||
|
||||
const filterTypes = (value: string) => {
|
||||
if (allowDecimals === false) value = value.replace(".", "");
|
||||
if (allowNegatives === false) value = value.replace("-", "");
|
||||
if (allowDecimals === false) value = value.replace('.', '');
|
||||
if (allowNegatives === false) value = value.replace('-', '');
|
||||
if (sigDigitsExceeded(value, maxSigDigits)) {
|
||||
value = value.substring(0, value.length - 1);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
const filterValue = (value: string) => {
|
||||
if (stringIsEmpty(value)) return "";
|
||||
if (stringIsEmpty(value)) return '';
|
||||
value = filterTypes(value);
|
||||
if (isFloatNum.test(value)) {
|
||||
return setMinMaxValue(value);
|
||||
@ -109,8 +109,8 @@ export const BoundedNumericTextField = ({
|
||||
|
||||
const formatValueOnBlur = (e: eventType) => {
|
||||
let value = e.target.value;
|
||||
if (stringIsEmpty(value) || value === ".") {
|
||||
setTextFieldValue("");
|
||||
if (stringIsEmpty(value) || value === '.') {
|
||||
setTextFieldValue('');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -129,23 +129,33 @@ export const BoundedNumericTextField = ({
|
||||
...props?.InputProps,
|
||||
endAdornment: addIconButtons ? (
|
||||
<InputAdornment position="end">
|
||||
<IconButton size="small" onClick={() => changeValueWithIncDecButton(1)}>
|
||||
<AddIcon sx={{
|
||||
color: 'white'
|
||||
}} />{" "}
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => changeValueWithIncDecButton(1)}
|
||||
>
|
||||
<AddIcon
|
||||
sx={{
|
||||
color: 'white',
|
||||
}}
|
||||
/>{' '}
|
||||
</IconButton>
|
||||
<IconButton size="small" onClick={() => changeValueWithIncDecButton(-1)}>
|
||||
<RemoveIcon sx={{
|
||||
color: 'white'
|
||||
}} />{" "}
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => changeValueWithIncDecButton(-1)}
|
||||
>
|
||||
<RemoveIcon
|
||||
sx={{
|
||||
color: 'white',
|
||||
}}
|
||||
/>{' '}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
) : (
|
||||
<></>
|
||||
),
|
||||
}}
|
||||
onChange={e => listeners(e as eventType)}
|
||||
onBlur={e => {
|
||||
onChange={(e) => listeners(e as eventType)}
|
||||
onBlur={(e) => {
|
||||
formatValueOnBlur(e as eventType);
|
||||
}}
|
||||
autoComplete="off"
|
||||
|
@ -1,57 +1,57 @@
|
||||
import React, { useState } from "react";
|
||||
import QRCode from "react-qr-code";
|
||||
import { TextP } from "../App-styles";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import React, { useState } from 'react';
|
||||
import QRCode from 'react-qr-code';
|
||||
import { TextP } from '../styles/App-styles';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export const AddressQRCode = ({ targetAddress }) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
marginTop: '10px'
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
marginTop: '10px',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
fontSize: "14px",
|
||||
cursor: 'pointer',
|
||||
fontSize: '14px',
|
||||
}}
|
||||
onClick={() => {
|
||||
setOpen((prev)=> !prev);
|
||||
setOpen((prev) => !prev);
|
||||
}}
|
||||
>
|
||||
{open ? 'Hide QR code' :'See QR code'}
|
||||
{open ? 'Hide QR code' : 'See QR code'}
|
||||
</Typography>
|
||||
|
||||
{open && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
marginTop: "20px",
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
marginTop: '20px',
|
||||
}}
|
||||
>
|
||||
<TextP
|
||||
sx={{
|
||||
textAlign: "center",
|
||||
textAlign: 'center',
|
||||
lineHeight: 1.2,
|
||||
fontSize: "16px",
|
||||
fontSize: '16px',
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
|
@ -1,78 +1,78 @@
|
||||
import { Typography, Box, ButtonBase } from "@mui/material";
|
||||
import { styled } from "@mui/system";
|
||||
import { Typography, Box, ButtonBase } from '@mui/material';
|
||||
import { styled } from '@mui/system';
|
||||
|
||||
export const AppsParent = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
flexDirection: "column",
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
overflow: "auto",
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
overflow: 'auto',
|
||||
// For WebKit-based browsers (Chrome, Safari, etc.)
|
||||
"::-webkit-scrollbar": {
|
||||
width: "0px", // Set the width to 0 to hide the scrollbar
|
||||
height: "0px", // Set the height to 0 for horizontal scrollbar
|
||||
'::-webkit-scrollbar': {
|
||||
width: '0px', // Set the width to 0 to hide the scrollbar
|
||||
height: '0px', // Set the height to 0 for horizontal scrollbar
|
||||
},
|
||||
|
||||
// For Firefox
|
||||
scrollbarWidth: "none", // Hides the scrollbar in Firefox
|
||||
scrollbarWidth: 'none', // Hides the scrollbar in Firefox
|
||||
|
||||
// Optional for better cross-browser consistency
|
||||
"-msOverflowStyle": "none", // Hides scrollbar in IE and Edge
|
||||
'-msOverflowStyle': 'none', // Hides scrollbar in IE and Edge
|
||||
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppsContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
width: "90%",
|
||||
justifyContent: "space-evenly",
|
||||
gap: "24px",
|
||||
flexWrap: "wrap",
|
||||
alignItems: "flex-start",
|
||||
alignSelf: "center",
|
||||
display: 'flex',
|
||||
width: '90%',
|
||||
justifyContent: 'space-evenly',
|
||||
gap: '24px',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'flex-start',
|
||||
alignSelf: 'center',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppsLibraryContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
}));
|
||||
|
||||
export const AppsWidthLimiter = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
width: "90%",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-start",
|
||||
display: 'flex',
|
||||
width: '90%',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'flex-start',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppsSearchContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
width: "90%",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
borderRadius: "8px",
|
||||
padding: "0px 10px",
|
||||
height: "36px",
|
||||
display: 'flex',
|
||||
width: '90%',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
borderRadius: '8px',
|
||||
padding: '0px 10px',
|
||||
height: '36px',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppsSearchLeft = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
width: "90%",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
display: 'flex',
|
||||
width: '90%',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
gap: '10px',
|
||||
flexGrow: 1,
|
||||
flexShrink: 0,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
@ -80,51 +80,50 @@ export const AppsSearchLeft = styled(Box)(({ theme }) => ({
|
||||
}));
|
||||
|
||||
export const AppsSearchRight = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
width: "90%",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
width: '90%',
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center',
|
||||
flexShrink: 1,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppCircleContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "5px",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '5px',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const Add = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "36px",
|
||||
fontSize: '36px',
|
||||
fontWeight: 500,
|
||||
lineHeight: "43.57px",
|
||||
textAlign: "left",
|
||||
lineHeight: '43.57px',
|
||||
textAlign: 'left',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppCircleLabel = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "14px",
|
||||
fontWeight: 500,
|
||||
lineHeight: 1.2,
|
||||
// whiteSpace: 'nowrap',
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
width: "120%",
|
||||
"-webkit-line-clamp": "2",
|
||||
"-webkit-box-orient": "vertical",
|
||||
display: "-webkit-box",
|
||||
'-webkit-box-orient': 'vertical',
|
||||
'-webkit-line-clamp': '2',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
display: '-webkit-box',
|
||||
fontSize: '14px',
|
||||
fontWeight: 500,
|
||||
lineHeight: 1.2,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
width: '120%',
|
||||
}));
|
||||
|
||||
export const AppLibrarySubTitle = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "16px",
|
||||
fontSize: '16px',
|
||||
fontWeight: 500,
|
||||
lineHeight: 1.2,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
@ -132,58 +131,63 @@ export const AppLibrarySubTitle = styled(Typography)(({ theme }) => ({
|
||||
}));
|
||||
|
||||
export const AppCircle = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
width: "75px",
|
||||
flexDirection: "column",
|
||||
height: "75px",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
borderRadius: "50%",
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
borderColor:
|
||||
theme.palette.mode === 'dark'
|
||||
? 'rgb(209, 209, 209)'
|
||||
: 'rgba(41, 41, 43, 1)',
|
||||
borderWidth: '1px',
|
||||
borderRadius: '50%',
|
||||
borderStyle: 'solid',
|
||||
color: theme.palette.text.primary,
|
||||
border: "1px solid #FFFFFF",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '75px',
|
||||
justifyContent: 'center',
|
||||
width: '75px',
|
||||
}));
|
||||
|
||||
export const AppInfoSnippetContainer = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
}));
|
||||
|
||||
export const AppInfoSnippetLeft = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
gap: "12px",
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
display: 'flex',
|
||||
gap: '12px',
|
||||
justifyContent: 'flex-start',
|
||||
}));
|
||||
|
||||
export const AppInfoSnippetRight = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppDownloadButton = styled(ButtonBase)(({ theme }) => ({
|
||||
backgroundColor: "#247C0E",
|
||||
backgroundColor: '#247C0E',
|
||||
color: theme.palette.text.primary,
|
||||
width: "101px",
|
||||
height: "29px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
borderRadius: "25px",
|
||||
alignSelf: "center",
|
||||
width: '101px',
|
||||
height: '29px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderRadius: '25px',
|
||||
alignSelf: 'center',
|
||||
}));
|
||||
|
||||
export const AppDownloadButtonText = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "14px",
|
||||
fontSize: '14px',
|
||||
fontWeight: 500,
|
||||
lineHeight: 1.2,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
@ -191,50 +195,50 @@ export const AppDownloadButtonText = styled(Typography)(({ theme }) => ({
|
||||
}));
|
||||
|
||||
export const AppPublishTagsContainer = styled(Box)(({ theme }) => ({
|
||||
gap: "10px",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "flex-start",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
gap: '10px',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'flex-start',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppInfoSnippetMiddle = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-start",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'flex-start',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppInfoAppName = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "16px",
|
||||
fontSize: '16px',
|
||||
fontWeight: 500,
|
||||
lineHeight: 1.2,
|
||||
textAlign: "start",
|
||||
textAlign: 'start',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppInfoUserName = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "13px",
|
||||
fontSize: '13px',
|
||||
fontWeight: 400,
|
||||
lineHeight: 1.2,
|
||||
textAlign: "start",
|
||||
textAlign: 'start',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppsNavBarParent = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
height: "60px",
|
||||
padding: "0px 10px",
|
||||
position: "fixed",
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
height: '60px',
|
||||
padding: '0px 10px',
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
zIndex: 1,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
@ -242,120 +246,120 @@ export const AppsNavBarParent = styled(Box)(({ theme }) => ({
|
||||
}));
|
||||
|
||||
export const AppsNavBarLeft = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
flexGrow: 1,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppsNavBarRight = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const TabParent = styled(Box)(({ theme }) => ({
|
||||
height: "36px",
|
||||
width: "36px",
|
||||
position: "relative",
|
||||
borderRadius: "50%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: '36px',
|
||||
width: '36px',
|
||||
position: 'relative',
|
||||
borderRadius: '50%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const PublishQAppCTAParent = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const PublishQAppCTALeft = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const PublishQAppCTARight = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const PublishQAppCTAButton = styled(ButtonBase)(({ theme }) => ({
|
||||
width: "101px",
|
||||
height: "29px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
borderRadius: "25px",
|
||||
border: "1px solid #FFFFFF",
|
||||
width: '101px',
|
||||
height: '29px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderRadius: '25px',
|
||||
border: '1px solid #FFFFFF',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const PublishQAppDotsBG = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
width: "60px",
|
||||
height: "60px",
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const PublishQAppInfo = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "10px",
|
||||
fontSize: '10px',
|
||||
fontWeight: 400,
|
||||
lineHeight: 1.2,
|
||||
fontStyle: "italic",
|
||||
fontStyle: 'italic',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const PublishQAppChoseFile = styled(ButtonBase)(({ theme }) => ({
|
||||
width: "101px",
|
||||
height: "30px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
borderRadius: "5px",
|
||||
width: '101px',
|
||||
height: '30px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderRadius: '5px',
|
||||
fontWeight: 600,
|
||||
fontSize: "10px",
|
||||
fontSize: '10px',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppsCategoryInfo = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppsCategoryInfoSub = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AppsCategoryInfoLabel = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "12px",
|
||||
fontSize: '12px',
|
||||
fontWeight: 700,
|
||||
lineHeight: 1.2,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
@ -363,7 +367,7 @@ export const AppsCategoryInfoLabel = styled(Typography)(({ theme }) => ({
|
||||
}));
|
||||
|
||||
export const AppsCategoryInfoValue = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "12px",
|
||||
fontSize: '12px',
|
||||
fontWeight: 500,
|
||||
lineHeight: 1.2,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
@ -371,11 +375,11 @@ export const AppsCategoryInfoValue = styled(Typography)(({ theme }) => ({
|
||||
}));
|
||||
|
||||
export const AppsInfoDescription = styled(Typography)(({ theme }) => ({
|
||||
fontSize: "13px",
|
||||
fontSize: '13px',
|
||||
fontWeight: 300,
|
||||
lineHeight: 1.2,
|
||||
width: "90%",
|
||||
textAlign: "start",
|
||||
width: '90%',
|
||||
textAlign: 'start',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
@ -1,46 +1,68 @@
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { AppsDevModeHome } from "./AppsDevModeHome";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { MyContext, getBaseApiReact } from "../../App";
|
||||
import { AppInfo } from "./AppInfo";
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { AppsDevModeHome } from './AppsDevModeHome';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
import { AppInfo } from './AppInfo';
|
||||
|
||||
import {
|
||||
executeEvent,
|
||||
subscribeToEvent,
|
||||
unsubscribeFromEvent,
|
||||
} from "../../utils/events";
|
||||
import { AppsParent } from "./Apps-styles";
|
||||
import AppViewerContainer from "./AppViewerContainer";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import { AppPublish } from "./AppPublish";
|
||||
import { AppsLibraryDesktop } from "./AppsLibraryDesktop";
|
||||
import { AppsCategoryDesktop } from "./AppsCategoryDesktop";
|
||||
import { AppsNavBarDesktop } from "./AppsNavBarDesktop";
|
||||
import { Box, ButtonBase } from "@mui/material";
|
||||
import { HomeIcon } from "../../assets/Icons/HomeIcon";
|
||||
import { MessagingIcon } from "../../assets/Icons/MessagingIcon";
|
||||
import { Save } from "../Save/Save";
|
||||
import { HubsIcon } from "../../assets/Icons/HubsIcon";
|
||||
import { AppsDevModeNavBar } from "./AppsDevModeNavBar";
|
||||
import { CoreSyncStatus } from "../CoreSyncStatus";
|
||||
import { AppsIcon } from "../../assets/Icons/AppsIcon";
|
||||
import { IconWrapper } from "../Desktop/DesktopFooter";
|
||||
} from '../../utils/events';
|
||||
import { AppsParent } from './Apps-styles';
|
||||
import AppViewerContainer from './AppViewerContainer';
|
||||
import ShortUniqueId from 'short-unique-id';
|
||||
import { AppPublish } from './AppPublish';
|
||||
import { AppsLibraryDesktop } from './AppsLibraryDesktop';
|
||||
import { AppsCategoryDesktop } from './AppsCategoryDesktop';
|
||||
import { AppsNavBarDesktop } from './AppsNavBarDesktop';
|
||||
import { Box, ButtonBase } from '@mui/material';
|
||||
import { HomeIcon } from '../../assets/Icons/HomeIcon';
|
||||
import { MessagingIcon } from '../../assets/Icons/MessagingIcon';
|
||||
import { Save } from '../Save/Save';
|
||||
import { HubsIcon } from '../../assets/Icons/HubsIcon';
|
||||
import { AppsDevModeNavBar } from './AppsDevModeNavBar';
|
||||
import { AppsIcon } from '../../assets/Icons/AppsIcon';
|
||||
import { IconWrapper } from '../Desktop/DesktopFooter';
|
||||
|
||||
const uid = new ShortUniqueId({ length: 8 });
|
||||
|
||||
export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktopSideView, hasUnreadDirects, isDirects, isGroups, hasUnreadGroups, toggleSideViewGroups, toggleSideViewDirects, setDesktopViewMode, desktopViewMode, isApps}) => {
|
||||
export const AppsDevMode = ({
|
||||
mode,
|
||||
setMode,
|
||||
show,
|
||||
myName,
|
||||
goToHome,
|
||||
setDesktopSideView,
|
||||
hasUnreadDirects,
|
||||
isDirects,
|
||||
isGroups,
|
||||
hasUnreadGroups,
|
||||
toggleSideViewGroups,
|
||||
toggleSideViewDirects,
|
||||
setDesktopViewMode,
|
||||
desktopViewMode,
|
||||
isApps,
|
||||
}) => {
|
||||
const [availableQapps, setAvailableQapps] = useState([]);
|
||||
const [selectedAppInfo, setSelectedAppInfo] = useState(null);
|
||||
const [selectedCategory, setSelectedCategory] = useState(null)
|
||||
const [selectedCategory, setSelectedCategory] = useState(null);
|
||||
const [tabs, setTabs] = useState([]);
|
||||
const [selectedTab, setSelectedTab] = useState(null);
|
||||
const [isNewTabWindow, setIsNewTabWindow] = useState(false);
|
||||
const [categories, setCategories] = useState([])
|
||||
const [categories, setCategories] = useState([]);
|
||||
const iframeRefs = useRef({});
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
executeEvent("appsDevModeSetTabsToNav", {
|
||||
executeEvent('appsDevModeSetTabsToNav', {
|
||||
data: {
|
||||
tabs: tabs,
|
||||
selectedTab: selectedTab,
|
||||
@ -50,17 +72,16 @@ export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktop
|
||||
}, 100);
|
||||
}, [show, tabs, selectedTab, isNewTabWindow]);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const navigateBackFunc = (e) => {
|
||||
if (['category', 'appInfo-from-category', 'appInfo', 'library', 'publish'].includes(mode)) {
|
||||
if (
|
||||
[
|
||||
'category',
|
||||
'appInfo-from-category',
|
||||
'appInfo',
|
||||
'library',
|
||||
'publish',
|
||||
].includes(mode)
|
||||
) {
|
||||
// Handle the various modes as needed
|
||||
if (mode === 'category') {
|
||||
setMode('library');
|
||||
@ -78,17 +99,16 @@ export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktop
|
||||
} else if (mode === 'publish') {
|
||||
setMode('library');
|
||||
}
|
||||
} else if(selectedTab?.tabId) {
|
||||
executeEvent(`navigateBackApp-${selectedTab?.tabId}`, {})
|
||||
} else if (selectedTab?.tabId) {
|
||||
executeEvent(`navigateBackApp-${selectedTab?.tabId}`, {});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("devModeNavigateBack", navigateBackFunc);
|
||||
subscribeToEvent('devModeNavigateBack', navigateBackFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("devModeNavigateBack", navigateBackFunc);
|
||||
unsubscribeFromEvent('devModeNavigateBack', navigateBackFunc);
|
||||
};
|
||||
}, [mode, selectedTab]);
|
||||
|
||||
@ -100,58 +120,52 @@ export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktop
|
||||
};
|
||||
setTabs((prev) => [...prev, newTab]);
|
||||
setSelectedTab(newTab);
|
||||
setMode("viewer");
|
||||
setMode('viewer');
|
||||
|
||||
setIsNewTabWindow(false);
|
||||
};
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("appsDevModeAddTab", addTabFunc);
|
||||
subscribeToEvent('appsDevModeAddTab', addTabFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("appsDevModeAddTab", addTabFunc);
|
||||
unsubscribeFromEvent('appsDevModeAddTab', addTabFunc);
|
||||
};
|
||||
}, [tabs]);
|
||||
|
||||
const updateTabFunc = (e) => {
|
||||
const data = e.detail?.data;
|
||||
if(!data.tabId) return
|
||||
const findIndexTab = tabs.findIndex((tab)=> tab?.tabId === data?.tabId)
|
||||
if(findIndexTab === -1) return
|
||||
const copyTabs = [...tabs]
|
||||
const newTab ={
|
||||
if (!data.tabId) return;
|
||||
const findIndexTab = tabs.findIndex((tab) => tab?.tabId === data?.tabId);
|
||||
if (findIndexTab === -1) return;
|
||||
const copyTabs = [...tabs];
|
||||
const newTab = {
|
||||
...copyTabs[findIndexTab],
|
||||
url: data.url
|
||||
url: data.url,
|
||||
};
|
||||
copyTabs[findIndexTab] = newTab;
|
||||
|
||||
}
|
||||
copyTabs[findIndexTab] = newTab
|
||||
|
||||
setTabs(copyTabs);
|
||||
setSelectedTab(newTab);
|
||||
setMode("viewer");
|
||||
setMode('viewer');
|
||||
|
||||
setIsNewTabWindow(false);
|
||||
};
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("appsDevModeUpdateTab", updateTabFunc);
|
||||
subscribeToEvent('appsDevModeUpdateTab', updateTabFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("appsDevModeUpdateTab", updateTabFunc);
|
||||
unsubscribeFromEvent('appsDevModeUpdateTab', updateTabFunc);
|
||||
};
|
||||
}, [tabs]);
|
||||
|
||||
|
||||
const setSelectedTabFunc = (e) => {
|
||||
const data = e.detail?.data;
|
||||
if(!e.detail?.isDevMode) return
|
||||
if (!e.detail?.isDevMode) return;
|
||||
setSelectedTab(data);
|
||||
setTimeout(() => {
|
||||
executeEvent("appsDevModeSetTabsToNav", {
|
||||
executeEvent('appsDevModeSetTabsToNav', {
|
||||
data: {
|
||||
tabs: tabs,
|
||||
selectedTab: data,
|
||||
@ -161,13 +175,12 @@ export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktop
|
||||
}, 100);
|
||||
setIsNewTabWindow(false);
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("setSelectedTabDevMode", setSelectedTabFunc);
|
||||
subscribeToEvent('setSelectedTabDevMode', setSelectedTabFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("setSelectedTabDevMode", setSelectedTabFunc);
|
||||
unsubscribeFromEvent('setSelectedTabDevMode', setSelectedTabFunc);
|
||||
};
|
||||
}, [tabs, isNewTabWindow]);
|
||||
|
||||
@ -175,14 +188,14 @@ export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktop
|
||||
const data = e.detail?.data;
|
||||
const copyTabs = [...tabs].filter((tab) => tab?.tabId !== data?.tabId);
|
||||
if (copyTabs?.length === 0) {
|
||||
setMode("home");
|
||||
setMode('home');
|
||||
} else {
|
||||
setSelectedTab(copyTabs[0]);
|
||||
}
|
||||
setTabs(copyTabs);
|
||||
setSelectedTab(copyTabs[0]);
|
||||
setTimeout(() => {
|
||||
executeEvent("appsDevModeSetTabsToNav", {
|
||||
executeEvent('appsDevModeSetTabsToNav', {
|
||||
data: {
|
||||
tabs: copyTabs,
|
||||
selectedTab: copyTabs[0],
|
||||
@ -192,143 +205,157 @@ export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktop
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("removeTabDevMode", removeTabFunc);
|
||||
subscribeToEvent('removeTabDevMode', removeTabFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("removeTabDevMode", removeTabFunc);
|
||||
unsubscribeFromEvent('removeTabDevMode', removeTabFunc);
|
||||
};
|
||||
}, [tabs]);
|
||||
|
||||
const setNewTabWindowFunc = (e) => {
|
||||
setIsNewTabWindow(true);
|
||||
setSelectedTab(null)
|
||||
setSelectedTab(null);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("devModeNewTabWindow", setNewTabWindowFunc);
|
||||
subscribeToEvent('devModeNewTabWindow', setNewTabWindowFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("devModeNewTabWindow", setNewTabWindowFunc);
|
||||
unsubscribeFromEvent('devModeNewTabWindow', setNewTabWindowFunc);
|
||||
};
|
||||
}, [tabs]);
|
||||
|
||||
|
||||
return (
|
||||
<AppsParent
|
||||
sx={{
|
||||
flexDirection: 'row' ,
|
||||
flexDirection: 'row',
|
||||
position: !show && 'fixed',
|
||||
left: !show && '-200vw',
|
||||
}}
|
||||
>
|
||||
|
||||
<Box sx={{
|
||||
width: '60px',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
gap: '25px'
|
||||
}}>
|
||||
<Box
|
||||
sx={{
|
||||
width: '60px',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
gap: '25px',
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
sx={{
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
paddingTop: '23px'
|
||||
paddingTop: '23px',
|
||||
}}
|
||||
onClick={() => {
|
||||
goToHome();
|
||||
|
||||
}}
|
||||
>
|
||||
|
||||
<HomeIcon
|
||||
height={34}
|
||||
color={desktopViewMode === 'home' ? 'white': "rgba(250, 250, 250, 0.5)"}
|
||||
/>
|
||||
|
||||
<HomeIcon
|
||||
height={34}
|
||||
color={
|
||||
desktopViewMode === 'home' ? 'white' : 'rgba(250, 250, 250, 0.5)'
|
||||
}
|
||||
/>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopViewMode('apps')
|
||||
setDesktopViewMode('apps');
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color={isApps ? 'white' :"rgba(250, 250, 250, 0.5)"}
|
||||
color={isApps ? 'white' : 'rgba(250, 250, 250, 0.5)'}
|
||||
label="Apps"
|
||||
disableWidth
|
||||
>
|
||||
<AppsIcon height={30} color={isApps ? 'white' :"rgba(250, 250, 250, 0.5)"} />
|
||||
<AppsIcon
|
||||
height={30}
|
||||
color={isApps ? 'white' : 'rgba(250, 250, 250, 0.5)'}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopViewMode('chat')
|
||||
setDesktopViewMode('chat');
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color={(hasUnreadDirects || hasUnreadGroups) ? "var(--unread)" : desktopViewMode === 'chat' ? 'white' :"rgba(250, 250, 250, 0.5)"}
|
||||
<IconWrapper
|
||||
color={
|
||||
hasUnreadDirects || hasUnreadGroups
|
||||
? 'var(--unread)'
|
||||
: desktopViewMode === 'chat'
|
||||
? 'white'
|
||||
: 'rgba(250, 250, 250, 0.5)'
|
||||
}
|
||||
label="Chat"
|
||||
disableWidth
|
||||
>
|
||||
<MessagingIcon
|
||||
height={30}
|
||||
color={
|
||||
(hasUnreadDirects || hasUnreadGroups)
|
||||
? "var(--unread)"
|
||||
hasUnreadDirects || hasUnreadGroups
|
||||
? 'var(--unread)'
|
||||
: desktopViewMode === 'chat'
|
||||
? "white"
|
||||
: "rgba(250, 250, 250, 0.5)"
|
||||
? 'white'
|
||||
: 'rgba(250, 250, 250, 0.5)'
|
||||
}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<Save isDesktop disableWidth myName={myName} />
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopViewMode('dev')
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color={desktopViewMode === 'dev' ? 'white' : "rgba(250, 250, 250, 0.5)"}
|
||||
label="Dev"
|
||||
disableWidth
|
||||
>
|
||||
<AppsIcon color={desktopViewMode === 'dev' ? 'white' : "rgba(250, 250, 250, 0.5)"} height={30} />
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
{mode !== 'home' && (
|
||||
<AppsDevModeNavBar />
|
||||
onClick={() => {
|
||||
setDesktopViewMode('dev');
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color={
|
||||
desktopViewMode === 'dev' ? 'white' : 'rgba(250, 250, 250, 0.5)'
|
||||
}
|
||||
label="Dev"
|
||||
disableWidth
|
||||
>
|
||||
<AppsIcon
|
||||
color={
|
||||
desktopViewMode === 'dev' ? 'white' : 'rgba(250, 250, 250, 0.5)'
|
||||
}
|
||||
height={30}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
{mode !== 'home' && <AppsDevModeNavBar />}
|
||||
</Box>
|
||||
|
||||
)}
|
||||
|
||||
</Box>
|
||||
|
||||
|
||||
{mode === "home" && (
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
overflow: 'auto'
|
||||
}}>
|
||||
|
||||
<Spacer height="30px" />
|
||||
<AppsDevModeHome myName={myName} availableQapps={availableQapps} setMode={setMode} myApp={null} myWebsite={null} />
|
||||
{mode === 'home' && (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<Spacer height="30px" />
|
||||
<AppsDevModeHome
|
||||
myName={myName}
|
||||
availableQapps={availableQapps}
|
||||
setMode={setMode}
|
||||
myApp={null}
|
||||
myWebsite={null}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{tabs.map((tab) => {
|
||||
if (!iframeRefs.current[tab.tabId]) {
|
||||
iframeRefs.current[tab.tabId] = React.createRef();
|
||||
}
|
||||
return (
|
||||
<AppViewerContainer
|
||||
key={tab?.tabId}
|
||||
key={tab?.tabId}
|
||||
hide={isNewTabWindow}
|
||||
isSelected={tab?.tabId === selectedTab?.tabId}
|
||||
app={tab}
|
||||
@ -338,18 +365,25 @@ export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktop
|
||||
);
|
||||
})}
|
||||
|
||||
{isNewTabWindow && mode === "viewer" && (
|
||||
{isNewTabWindow && mode === 'viewer' && (
|
||||
<>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
overflow: 'auto'
|
||||
}}>
|
||||
|
||||
<Spacer height="30px" />
|
||||
<AppsDevModeHome myName={myName} availableQapps={availableQapps} setMode={setMode} myApp={null} myWebsite={null} />
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<Spacer height="30px" />
|
||||
<AppsDevModeHome
|
||||
myName={myName}
|
||||
availableQapps={availableQapps}
|
||||
setMode={setMode}
|
||||
myApp={null}
|
||||
myWebsite={null}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
|
@ -1,22 +1,21 @@
|
||||
import React from "react";
|
||||
import { TabParent } from "./Apps-styles";
|
||||
import NavCloseTab from "../../assets/svgs/NavCloseTab.svg";
|
||||
import { getBaseApiReact } from "../../App";
|
||||
import { Avatar, ButtonBase } from "@mui/material";
|
||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { TabParent } from './Apps-styles';
|
||||
import NavCloseTab from '../../assets/svgs/NavCloseTab.svg';
|
||||
import { getBaseApiReact } from '../../App';
|
||||
import { Avatar, ButtonBase } from '@mui/material';
|
||||
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
|
||||
export const AppsDevModeTabComponent = ({ isSelected, app }) => {
|
||||
return (
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
if (isSelected) {
|
||||
executeEvent("removeTabDevMode", {
|
||||
executeEvent('removeTabDevMode', {
|
||||
data: app,
|
||||
});
|
||||
return;
|
||||
}
|
||||
executeEvent("setSelectedTabDevMode", {
|
||||
executeEvent('setSelectedTabDevMode', {
|
||||
data: app,
|
||||
isDevMode: true,
|
||||
});
|
||||
@ -24,15 +23,15 @@ export const AppsDevModeTabComponent = ({ isSelected, app }) => {
|
||||
>
|
||||
<TabParent
|
||||
sx={{
|
||||
border: isSelected && "1px solid #FFFFFF",
|
||||
border: isSelected && '1px solid #FFFFFF',
|
||||
}}
|
||||
>
|
||||
{isSelected && (
|
||||
<img
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "-5px",
|
||||
right: "-5px",
|
||||
position: 'absolute',
|
||||
top: '-5px',
|
||||
right: '-5px',
|
||||
zIndex: 1,
|
||||
}}
|
||||
src={NavCloseTab}
|
||||
@ -40,23 +39,25 @@ export const AppsDevModeTabComponent = ({ isSelected, app }) => {
|
||||
)}
|
||||
<Avatar
|
||||
sx={{
|
||||
height: "28px",
|
||||
width: "28px",
|
||||
height: '28px',
|
||||
width: '28px',
|
||||
}}
|
||||
alt=""
|
||||
src={``}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: "28px",
|
||||
height: "auto",
|
||||
width: '28px',
|
||||
height: 'auto',
|
||||
}}
|
||||
src={ app?.customIcon ? app?.customIcon :
|
||||
app?.service
|
||||
? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||
app?.name
|
||||
}/qortal_avatar?async=true`
|
||||
: LogoSelected
|
||||
src={
|
||||
app?.customIcon
|
||||
? app?.customIcon
|
||||
: app?.service
|
||||
? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||
app?.name
|
||||
}/qortal_avatar?async=true`
|
||||
: LogoSelected
|
||||
}
|
||||
alt="center-icon"
|
||||
/>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useMemo, useState } from "react";
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
@ -6,52 +6,51 @@ import {
|
||||
AppLibrarySubTitle,
|
||||
AppsContainer,
|
||||
AppsParent,
|
||||
} from "./Apps-styles";
|
||||
import { Avatar, ButtonBase } from "@mui/material";
|
||||
import { Add } from "@mui/icons-material";
|
||||
import { getBaseApiReact, isMobile } from "../../App";
|
||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { SortablePinnedApps } from "./SortablePinnedApps";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
} from './Apps-styles';
|
||||
import { Avatar, ButtonBase } from '@mui/material';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import { getBaseApiReact, isMobile } from '../../App';
|
||||
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { SortablePinnedApps } from './SortablePinnedApps';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
|
||||
export const AppsHome = ({ setMode, myApp, myWebsite, availableQapps }) => {
|
||||
export const AppsHome = ({ setMode, myApp, myWebsite, availableQapps }) => {
|
||||
return (
|
||||
<>
|
||||
<AppsContainer
|
||||
<AppsContainer
|
||||
sx={{
|
||||
|
||||
justifyContent: "flex-start",
|
||||
justifyContent: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<AppLibrarySubTitle
|
||||
|
||||
>
|
||||
Apps Dashboard
|
||||
|
||||
</AppLibrarySubTitle>
|
||||
</AppsContainer>
|
||||
<Spacer height="20px" />
|
||||
<AppLibrarySubTitle>Apps Dashboard</AppLibrarySubTitle>
|
||||
</AppsContainer>
|
||||
<Spacer height="20px" />
|
||||
|
||||
<AppsContainer>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setMode("library");
|
||||
setMode('library');
|
||||
}}
|
||||
>
|
||||
<AppCircleContainer sx={{
|
||||
gap: !isMobile ? "10px" : "5px",
|
||||
}}>
|
||||
<AppCircleContainer
|
||||
sx={{
|
||||
gap: !isMobile ? '10px' : '5px',
|
||||
}}
|
||||
>
|
||||
<AppCircle>
|
||||
<Add>+</Add>
|
||||
</AppCircle>
|
||||
<AppCircleLabel>Library</AppCircleLabel>
|
||||
</AppCircleContainer>
|
||||
</ButtonBase>
|
||||
|
||||
<SortablePinnedApps availableQapps={availableQapps} myWebsite={myWebsite} myApp={myApp} />
|
||||
|
||||
|
||||
<SortablePinnedApps
|
||||
availableQapps={availableQapps}
|
||||
myWebsite={myWebsite}
|
||||
myApp={myApp}
|
||||
/>
|
||||
</AppsContainer>
|
||||
</>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,21 +1,22 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
AppCircleLabel,
|
||||
AppLibrarySubTitle,
|
||||
AppsContainer,
|
||||
} from "./Apps-styles";
|
||||
import { Box, ButtonBase, Input } from "@mui/material";
|
||||
import { Add } from "@mui/icons-material";
|
||||
import { isMobile } from "../../App";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { SortablePinnedApps } from "./SortablePinnedApps";
|
||||
import { extractComponents } from "../Chat/MessageDisplay";
|
||||
import ArrowOutwardIcon from "@mui/icons-material/ArrowOutward";
|
||||
import { AppsPrivate } from "./AppsPrivate";
|
||||
import ThemeSelector from "../Theme/ThemeSelector";
|
||||
} from './Apps-styles';
|
||||
import { Box, ButtonBase, Input, useTheme } from '@mui/material';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import { isMobile } from '../../App';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { SortablePinnedApps } from './SortablePinnedApps';
|
||||
import { extractComponents } from '../Chat/MessageDisplay';
|
||||
import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward';
|
||||
import { AppsPrivate } from './AppsPrivate';
|
||||
import ThemeSelector from '../Theme/ThemeSelector';
|
||||
|
||||
export const AppsHomeDesktop = ({
|
||||
setMode,
|
||||
myApp,
|
||||
@ -23,7 +24,8 @@ export const AppsHomeDesktop = ({
|
||||
availableQapps,
|
||||
myName,
|
||||
}) => {
|
||||
const [qortalUrl, setQortalUrl] = useState("");
|
||||
const [qortalUrl, setQortalUrl] = useState('');
|
||||
const theme = useTheme();
|
||||
|
||||
const openQortalUrl = () => {
|
||||
try {
|
||||
@ -31,22 +33,24 @@ export const AppsHomeDesktop = ({
|
||||
const res = extractComponents(qortalUrl);
|
||||
if (res) {
|
||||
const { service, name, identifier, path } = res;
|
||||
executeEvent("addTab", { data: { service, name, identifier, path } });
|
||||
executeEvent("open-apps-mode", {});
|
||||
setQortalUrl("qortal://");
|
||||
executeEvent('addTab', { data: { service, name, identifier, path } });
|
||||
executeEvent('open-apps-mode', {});
|
||||
setQortalUrl('qortal://');
|
||||
}
|
||||
} catch (error) {}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<AppsContainer
|
||||
sx={{
|
||||
justifyContent: "flex-start",
|
||||
justifyContent: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<AppLibrarySubTitle
|
||||
sx={{
|
||||
fontSize: "30px",
|
||||
fontSize: '30px',
|
||||
}}
|
||||
>
|
||||
Apps Dashboard
|
||||
@ -57,19 +61,22 @@ export const AppsHomeDesktop = ({
|
||||
|
||||
<AppsContainer
|
||||
sx={{
|
||||
justifyContent: "flex-start",
|
||||
justifyContent: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#1f2023",
|
||||
padding: "7px",
|
||||
borderRadius: "20px",
|
||||
width: "100%",
|
||||
maxWidth: "500px",
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
alignItems: 'center',
|
||||
backgroundColor:
|
||||
theme.palette.mode === 'dark'
|
||||
? 'rgba(41, 41, 43, 1)'
|
||||
: 'rgb(209, 209, 209)',
|
||||
padding: '7px',
|
||||
borderRadius: '20px',
|
||||
width: '100%',
|
||||
maxWidth: '500px',
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
@ -83,24 +90,24 @@ export const AppsHomeDesktop = ({
|
||||
autoCorrect="off"
|
||||
placeholder="qortal://"
|
||||
sx={{
|
||||
width: "100%",
|
||||
color: "white",
|
||||
"& .MuiInput-input::placeholder": {
|
||||
color: "rgba(84, 84, 84, 0.70) !important",
|
||||
fontSize: "20px",
|
||||
fontStyle: "normal",
|
||||
width: '100%',
|
||||
color: theme.palette.mode === 'dark' ? 'white' : 'black',
|
||||
'& .MuiInput-input::placeholder': {
|
||||
color: 'rgba(84, 84, 84, 0.70) !important',
|
||||
fontSize: '20px',
|
||||
fontStyle: 'normal',
|
||||
fontWeight: 400,
|
||||
lineHeight: "120%", // 24px
|
||||
letterSpacing: "0.15px",
|
||||
lineHeight: '120%', // 24px
|
||||
letterSpacing: '0.15px',
|
||||
opacity: 1,
|
||||
},
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
'&:focus': {
|
||||
outline: 'none',
|
||||
},
|
||||
// Add any additional styles for the input here
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" && qortalUrl) {
|
||||
if (e.key === 'Enter' && qortalUrl) {
|
||||
openQortalUrl();
|
||||
}
|
||||
}}
|
||||
@ -108,7 +115,7 @@ export const AppsHomeDesktop = ({
|
||||
<ButtonBase onClick={() => openQortalUrl()}>
|
||||
<ArrowOutwardIcon
|
||||
sx={{
|
||||
color: qortalUrl ? "white" : "rgba(84, 84, 84, 0.70)",
|
||||
color: qortalUrl ? 'white' : 'rgba(84, 84, 84, 0.70)',
|
||||
}}
|
||||
/>
|
||||
</ButtonBase>
|
||||
@ -119,18 +126,18 @@ export const AppsHomeDesktop = ({
|
||||
|
||||
<AppsContainer
|
||||
sx={{
|
||||
gap: "50px",
|
||||
justifyContent: "flex-start",
|
||||
gap: '50px',
|
||||
justifyContent: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setMode("library");
|
||||
setMode('library');
|
||||
}}
|
||||
>
|
||||
<AppCircleContainer
|
||||
sx={{
|
||||
gap: !isMobile ? "10px" : "5px",
|
||||
gap: !isMobile ? '10px' : '5px',
|
||||
}}
|
||||
>
|
||||
<AppCircle>
|
||||
@ -150,7 +157,7 @@ export const AppsHomeDesktop = ({
|
||||
/>
|
||||
</AppsContainer>
|
||||
|
||||
<ThemeSelector style={{ position: "fixed", bottom: "1%", left: "0%" }} />
|
||||
<ThemeSelector style={{ position: 'fixed', bottom: '1%', left: '0%' }} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,11 @@
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
@ -16,84 +23,97 @@ import {
|
||||
PublishQAppCTAParent,
|
||||
PublishQAppCTARight,
|
||||
PublishQAppDotsBG,
|
||||
} from "./Apps-styles";
|
||||
import { Avatar, Box, ButtonBase, InputBase, styled } from "@mui/material";
|
||||
import { Add } from "@mui/icons-material";
|
||||
import { MyContext, getBaseApiReact } from "../../App";
|
||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
import IconSearch from "../../assets/svgs/Search.svg";
|
||||
import IconClearInput from "../../assets/svgs/ClearInput.svg";
|
||||
import qappDevelopText from "../../assets/svgs/qappDevelopText.svg";
|
||||
import qappDots from "../../assets/svgs/qappDots.svg";
|
||||
import ReturnSVG from '../../assets/svgs/Return.svg'
|
||||
} from './Apps-styles';
|
||||
import { Avatar, Box, ButtonBase, InputBase, styled } from '@mui/material';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||
import IconSearch from '../../assets/svgs/Search.svg';
|
||||
import IconClearInput from '../../assets/svgs/ClearInput.svg';
|
||||
import qappDevelopText from '../../assets/svgs/qappDevelopText.svg';
|
||||
import qappDots from '../../assets/svgs/qappDots.svg';
|
||||
import ReturnSVG from '../../assets/svgs/Return.svg';
|
||||
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { AppInfoSnippet } from "./AppInfoSnippet";
|
||||
import { Virtuoso } from "react-virtuoso";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { ComposeP, MailIconImg, ShowMessageReturnButton } from "../Group/Forum/Mail-styles";
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { AppInfoSnippet } from './AppInfoSnippet';
|
||||
import { Virtuoso } from 'react-virtuoso';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import {
|
||||
ComposeP,
|
||||
MailIconImg,
|
||||
ShowMessageReturnButton,
|
||||
} from '../Group/Forum/Mail-styles';
|
||||
const officialAppList = [
|
||||
"q-tube",
|
||||
"q-blog",
|
||||
"q-share",
|
||||
"q-support",
|
||||
"q-mail",
|
||||
"q-fund",
|
||||
"q-shop",
|
||||
"q-trade",
|
||||
"q-support",
|
||||
"q-manager",
|
||||
"q-wallets",
|
||||
"q-search"
|
||||
'q-tube',
|
||||
'q-blog',
|
||||
'q-share',
|
||||
'q-support',
|
||||
'q-mail',
|
||||
'q-fund',
|
||||
'q-shop',
|
||||
'q-trade',
|
||||
'q-support',
|
||||
'q-manager',
|
||||
'q-wallets',
|
||||
'q-search',
|
||||
];
|
||||
|
||||
const ScrollerStyled = styled('div')({
|
||||
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
||||
"::-webkit-scrollbar": {
|
||||
width: "0px",
|
||||
height: "0px",
|
||||
},
|
||||
|
||||
// Hide scrollbar for Firefox
|
||||
scrollbarWidth: "none",
|
||||
|
||||
// Hide scrollbar for IE and older Edge
|
||||
"-msOverflowStyle": "none",
|
||||
});
|
||||
|
||||
const StyledVirtuosoContainer = styled('div')({
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
|
||||
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
||||
"::-webkit-scrollbar": {
|
||||
width: "0px",
|
||||
height: "0px",
|
||||
},
|
||||
|
||||
// Hide scrollbar for Firefox
|
||||
scrollbarWidth: "none",
|
||||
|
||||
// Hide scrollbar for IE and older Edge
|
||||
"-msOverflowStyle": "none",
|
||||
});
|
||||
// TODO: apply dark/light style
|
||||
|
||||
export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, isShow, categories={categories} }) => {
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const ScrollerStyled = styled('div')({
|
||||
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
||||
'::-webkit-scrollbar': {
|
||||
width: '0px',
|
||||
height: '0px',
|
||||
},
|
||||
|
||||
// Hide scrollbar for Firefox
|
||||
scrollbarWidth: 'none',
|
||||
|
||||
// Hide scrollbar for IE and older Edge
|
||||
'-msOverflowStyle': 'none',
|
||||
});
|
||||
|
||||
const StyledVirtuosoContainer = styled('div')({
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
|
||||
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
||||
'::-webkit-scrollbar': {
|
||||
width: '0px',
|
||||
height: '0px',
|
||||
},
|
||||
|
||||
// Hide scrollbar for Firefox
|
||||
scrollbarWidth: 'none',
|
||||
|
||||
// Hide scrollbar for IE and older Edge
|
||||
'-msOverflowStyle': 'none',
|
||||
});
|
||||
|
||||
export const AppsLibrary = ({
|
||||
availableQapps,
|
||||
setMode,
|
||||
myName,
|
||||
hasPublishApp,
|
||||
isShow,
|
||||
categories = { categories },
|
||||
}) => {
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const virtuosoRef = useRef();
|
||||
const { rootHeight } = useContext(MyContext);
|
||||
|
||||
const officialApps = useMemo(() => {
|
||||
return availableQapps.filter(
|
||||
(app) =>
|
||||
app.service === "APP" &&
|
||||
app.service === 'APP' &&
|
||||
officialAppList.includes(app?.name?.toLowerCase())
|
||||
);
|
||||
}, [availableQapps]);
|
||||
|
||||
const [debouncedValue, setDebouncedValue] = useState(""); // Debounced value
|
||||
const [debouncedValue, setDebouncedValue] = useState(''); // Debounced value
|
||||
|
||||
// Debounce logic
|
||||
useEffect(() => {
|
||||
@ -117,23 +137,28 @@ export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, i
|
||||
}, [debouncedValue]);
|
||||
|
||||
const rowRenderer = (index) => {
|
||||
|
||||
let app = searchedList[index];
|
||||
return <AppInfoSnippet key={`${app?.service}-${app?.name}`} app={app} myName={myName} />;
|
||||
return (
|
||||
<AppInfoSnippet
|
||||
key={`${app?.service}-${app?.name}`}
|
||||
app={app}
|
||||
myName={myName}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<AppsLibraryContainer sx={{
|
||||
display: !isShow && 'none'
|
||||
}}>
|
||||
<AppsWidthLimiter>
|
||||
<AppsLibraryContainer
|
||||
sx={{
|
||||
display: !isShow && 'none',
|
||||
}}
|
||||
>
|
||||
<AppsWidthLimiter>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
justifyContent: "center",
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<AppsSearchContainer>
|
||||
@ -145,8 +170,8 @@ export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, i
|
||||
sx={{ ml: 1, flex: 1 }}
|
||||
placeholder="Search for apps"
|
||||
inputProps={{
|
||||
"aria-label": "Search for apps",
|
||||
fontSize: "16px",
|
||||
'aria-label': 'Search for apps',
|
||||
fontSize: '16px',
|
||||
fontWeight: 400,
|
||||
}}
|
||||
/>
|
||||
@ -155,7 +180,7 @@ export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, i
|
||||
{searchValue && (
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setSearchValue("");
|
||||
setSearchValue('');
|
||||
}}
|
||||
>
|
||||
<img src={IconClearInput} />
|
||||
@ -164,23 +189,27 @@ export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, i
|
||||
</AppsSearchRight>
|
||||
</AppsSearchContainer>
|
||||
</Box>
|
||||
</AppsWidthLimiter>
|
||||
<Spacer height="25px" />
|
||||
<ShowMessageReturnButton sx={{
|
||||
padding: '2px'
|
||||
}} onClick={() => {
|
||||
executeEvent("navigateBack", {});
|
||||
|
||||
}}>
|
||||
<MailIconImg src={ReturnSVG} />
|
||||
<ComposeP>Return to Apps Dashboard</ComposeP>
|
||||
</ShowMessageReturnButton>
|
||||
<Spacer height="25px" />
|
||||
{searchedList?.length > 0 ? (
|
||||
<AppsWidthLimiter>
|
||||
<StyledVirtuosoContainer sx={{
|
||||
height: rootHeight
|
||||
}}>
|
||||
</AppsWidthLimiter>
|
||||
<Spacer height="25px" />
|
||||
<ShowMessageReturnButton
|
||||
sx={{
|
||||
padding: '2px',
|
||||
}}
|
||||
onClick={() => {
|
||||
executeEvent('navigateBack', {});
|
||||
}}
|
||||
>
|
||||
<MailIconImg src={ReturnSVG} />
|
||||
<ComposeP>Return to Apps Dashboard</ComposeP>
|
||||
</ShowMessageReturnButton>
|
||||
<Spacer height="25px" />
|
||||
{searchedList?.length > 0 ? (
|
||||
<AppsWidthLimiter>
|
||||
<StyledVirtuosoContainer
|
||||
sx={{
|
||||
height: rootHeight,
|
||||
}}
|
||||
>
|
||||
<Virtuoso
|
||||
ref={virtuosoRef}
|
||||
data={searchedList}
|
||||
@ -188,14 +217,14 @@ export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, i
|
||||
atBottomThreshold={50}
|
||||
followOutput="smooth"
|
||||
components={{
|
||||
Scroller: ScrollerStyled // Use the styled scroller component
|
||||
Scroller: ScrollerStyled, // Use the styled scroller component
|
||||
}}
|
||||
/>
|
||||
</StyledVirtuosoContainer>
|
||||
</AppsWidthLimiter>
|
||||
) : (
|
||||
<>
|
||||
<AppsWidthLimiter>
|
||||
</AppsWidthLimiter>
|
||||
) : (
|
||||
<>
|
||||
<AppsWidthLimiter>
|
||||
<AppLibrarySubTitle>Official Apps</AppLibrarySubTitle>
|
||||
<Spacer height="18px" />
|
||||
<AppsContainer>
|
||||
@ -203,14 +232,14 @@ export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, i
|
||||
return (
|
||||
<ButtonBase
|
||||
sx={{
|
||||
height: "80px",
|
||||
width: "60px",
|
||||
height: '80px',
|
||||
width: '60px',
|
||||
}}
|
||||
onClick={()=> {
|
||||
onClick={() => {
|
||||
// executeEvent("addTab", {
|
||||
// data: qapp
|
||||
// })
|
||||
executeEvent("selectedAppInfo", {
|
||||
executeEvent('selectedAppInfo', {
|
||||
data: qapp,
|
||||
});
|
||||
}}
|
||||
@ -218,13 +247,13 @@ export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, i
|
||||
<AppCircleContainer>
|
||||
<AppCircle
|
||||
sx={{
|
||||
border: "none",
|
||||
border: 'none',
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
sx={{
|
||||
height: "31px",
|
||||
width: "31px",
|
||||
height: '31px',
|
||||
width: '31px',
|
||||
}}
|
||||
alt={qapp?.name}
|
||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||
@ -233,8 +262,8 @@ export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, i
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: "31px",
|
||||
height: "auto",
|
||||
width: '31px',
|
||||
height: 'auto',
|
||||
}}
|
||||
src={LogoSelected}
|
||||
alt="center-icon"
|
||||
@ -250,77 +279,87 @@ export const AppsLibrary = ({ availableQapps, setMode, myName, hasPublishApp, i
|
||||
})}
|
||||
</AppsContainer>
|
||||
<Spacer height="30px" />
|
||||
<AppLibrarySubTitle>{hasPublishApp ? 'Update Apps!' : 'Create Apps!'}</AppLibrarySubTitle>
|
||||
<AppLibrarySubTitle>
|
||||
{hasPublishApp ? 'Update Apps!' : 'Create Apps!'}
|
||||
</AppLibrarySubTitle>
|
||||
<Spacer height="18px" />
|
||||
</AppsWidthLimiter>
|
||||
<PublishQAppCTAParent>
|
||||
<PublishQAppCTALeft>
|
||||
<PublishQAppDotsBG>
|
||||
|
||||
<img src={qappDots} />
|
||||
</AppsWidthLimiter>
|
||||
<PublishQAppCTAParent>
|
||||
<PublishQAppCTALeft>
|
||||
<PublishQAppDotsBG>
|
||||
<img src={qappDots} />
|
||||
</PublishQAppDotsBG>
|
||||
<Spacer width="29px" />
|
||||
<Spacer width="29px" />
|
||||
<img src={qappDevelopText} />
|
||||
</PublishQAppCTALeft>
|
||||
<PublishQAppCTARight onClick={()=> {
|
||||
setMode('publish')
|
||||
}}>
|
||||
<PublishQAppCTAButton>
|
||||
{hasPublishApp ? 'Update' : 'Publish'}
|
||||
</PublishQAppCTAButton>
|
||||
<Spacer width="20px" />
|
||||
</PublishQAppCTARight>
|
||||
</PublishQAppCTAParent>
|
||||
<AppsWidthLimiter>
|
||||
|
||||
</PublishQAppCTALeft>
|
||||
<PublishQAppCTARight
|
||||
onClick={() => {
|
||||
setMode('publish');
|
||||
}}
|
||||
>
|
||||
<PublishQAppCTAButton>
|
||||
{hasPublishApp ? 'Update' : 'Publish'}
|
||||
</PublishQAppCTAButton>
|
||||
<Spacer width="20px" />
|
||||
</PublishQAppCTARight>
|
||||
</PublishQAppCTAParent>
|
||||
<AppsWidthLimiter>
|
||||
<Spacer height="18px" />
|
||||
<AppLibrarySubTitle>Categories</AppLibrarySubTitle>
|
||||
<Spacer height="18px" />
|
||||
<AppsWidthLimiter sx={{
|
||||
flexDirection: 'row',
|
||||
overflowX: 'auto',
|
||||
width: '100%',
|
||||
gap: '5px',
|
||||
"::-webkit-scrollbar": {
|
||||
width: "0px",
|
||||
height: "0px",
|
||||
},
|
||||
|
||||
// Hide scrollbar for Firefox
|
||||
scrollbarWidth: "none",
|
||||
|
||||
// Hide scrollbar for IE and older Edge
|
||||
"-msOverflowStyle": "none",
|
||||
}}>
|
||||
{categories?.map((category)=> {
|
||||
return (
|
||||
<ButtonBase key={category?.id} onClick={()=> {
|
||||
executeEvent('selectedCategory', {
|
||||
data: category
|
||||
})
|
||||
}}>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: '110px',
|
||||
width: '110px',
|
||||
background: 'linear-gradient(163.47deg, #4BBCFE 27.55%, #1386C9 86.56%)',
|
||||
color: '#1D1D1E',
|
||||
fontWeight: 700,
|
||||
fontSize: '16px',
|
||||
flexShrink: 0,
|
||||
borderRadius: '11px'
|
||||
}}>
|
||||
{category?.name}
|
||||
</Box>
|
||||
</ButtonBase>
|
||||
)
|
||||
})}
|
||||
<AppsWidthLimiter
|
||||
sx={{
|
||||
flexDirection: 'row',
|
||||
overflowX: 'auto',
|
||||
width: '100%',
|
||||
gap: '5px',
|
||||
'::-webkit-scrollbar': {
|
||||
width: '0px',
|
||||
height: '0px',
|
||||
},
|
||||
|
||||
// Hide scrollbar for Firefox
|
||||
scrollbarWidth: 'none',
|
||||
|
||||
// Hide scrollbar for IE and older Edge
|
||||
'-msOverflowStyle': 'none',
|
||||
}}
|
||||
>
|
||||
{categories?.map((category) => {
|
||||
return (
|
||||
<ButtonBase
|
||||
key={category?.id}
|
||||
onClick={() => {
|
||||
executeEvent('selectedCategory', {
|
||||
data: category,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: '110px',
|
||||
width: '110px',
|
||||
background:
|
||||
'linear-gradient(163.47deg, #4BBCFE 27.55%, #1386C9 86.56%)',
|
||||
color: '#1D1D1E',
|
||||
fontWeight: 700,
|
||||
fontSize: '16px',
|
||||
flexShrink: 0,
|
||||
borderRadius: '11px',
|
||||
}}
|
||||
>
|
||||
{category?.name}
|
||||
</Box>
|
||||
</ButtonBase>
|
||||
);
|
||||
})}
|
||||
</AppsWidthLimiter>
|
||||
</AppsWidthLimiter>
|
||||
</>
|
||||
)}
|
||||
</AppsLibraryContainer>
|
||||
</AppsWidthLimiter>
|
||||
</>
|
||||
)}
|
||||
</AppsLibraryContainer>
|
||||
);
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ import React, {
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
} from 'react';
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
@ -23,77 +23,87 @@ import {
|
||||
PublishQAppCTAParent,
|
||||
PublishQAppCTARight,
|
||||
PublishQAppDotsBG,
|
||||
} from "./Apps-styles";
|
||||
import { Avatar, Box, ButtonBase, InputBase, Typography, styled } from "@mui/material";
|
||||
import { Add } from "@mui/icons-material";
|
||||
import { MyContext, getBaseApiReact } from "../../App";
|
||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
import IconSearch from "../../assets/svgs/Search.svg";
|
||||
import IconClearInput from "../../assets/svgs/ClearInput.svg";
|
||||
import qappDevelopText from "../../assets/svgs/qappDevelopText.svg";
|
||||
import qappLibraryText from "../../assets/svgs/qappLibraryText.svg";
|
||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||
} from './Apps-styles';
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
ButtonBase,
|
||||
InputBase,
|
||||
Typography,
|
||||
styled,
|
||||
} from '@mui/material';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||
import IconSearch from '../../assets/svgs/Search.svg';
|
||||
import IconClearInput from '../../assets/svgs/ClearInput.svg';
|
||||
import qappDevelopText from '../../assets/svgs/qappDevelopText.svg';
|
||||
import qappLibraryText from '../../assets/svgs/qappLibraryText.svg';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
|
||||
import qappDots from "../../assets/svgs/qappDots.svg";
|
||||
import qappDots from '../../assets/svgs/qappDots.svg';
|
||||
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { AppInfoSnippet } from "./AppInfoSnippet";
|
||||
import { Virtuoso } from "react-virtuoso";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { AppInfoSnippet } from './AppInfoSnippet';
|
||||
import { Virtuoso } from 'react-virtuoso';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import {
|
||||
AppsDesktopLibraryBody,
|
||||
AppsDesktopLibraryHeader,
|
||||
} from "./AppsDesktop-styles";
|
||||
import { AppsNavBarDesktop } from "./AppsNavBarDesktop";
|
||||
import ReturnSVG from '../../assets/svgs/Return.svg'
|
||||
import { ComposeP, MailIconImg, ShowMessageReturnButton } from "../Group/Forum/Mail-styles";
|
||||
} from './AppsDesktop-styles';
|
||||
import ReturnSVG from '../../assets/svgs/Return.svg';
|
||||
import {
|
||||
ComposeP,
|
||||
MailIconImg,
|
||||
ShowMessageReturnButton,
|
||||
} from '../Group/Forum/Mail-styles';
|
||||
const officialAppList = [
|
||||
"q-tube",
|
||||
"q-blog",
|
||||
"q-share",
|
||||
"q-support",
|
||||
"q-mail",
|
||||
"q-fund",
|
||||
"q-shop",
|
||||
"q-trade",
|
||||
"q-support",
|
||||
"q-manager",
|
||||
"q-mintership",
|
||||
"q-wallets",
|
||||
"q-search"
|
||||
'q-tube',
|
||||
'q-blog',
|
||||
'q-share',
|
||||
'q-support',
|
||||
'q-mail',
|
||||
'q-fund',
|
||||
'q-shop',
|
||||
'q-trade',
|
||||
'q-support',
|
||||
'q-manager',
|
||||
'q-mintership',
|
||||
'q-wallets',
|
||||
'q-search',
|
||||
];
|
||||
|
||||
const ScrollerStyled = styled("div")({
|
||||
const ScrollerStyled = styled('div')({
|
||||
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
||||
"::-webkit-scrollbar": {
|
||||
width: "0px",
|
||||
height: "0px",
|
||||
'::-webkit-scrollbar': {
|
||||
width: '0px',
|
||||
height: '0px',
|
||||
},
|
||||
|
||||
// Hide scrollbar for Firefox
|
||||
scrollbarWidth: "none",
|
||||
scrollbarWidth: 'none',
|
||||
|
||||
// Hide scrollbar for IE and older Edge
|
||||
"-msOverflowStyle": "none",
|
||||
'-msOverflowStyle': 'none',
|
||||
});
|
||||
|
||||
const StyledVirtuosoContainer = styled("div")({
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
const StyledVirtuosoContainer = styled('div')({
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
|
||||
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
||||
"::-webkit-scrollbar": {
|
||||
width: "0px",
|
||||
height: "0px",
|
||||
'::-webkit-scrollbar': {
|
||||
width: '0px',
|
||||
height: '0px',
|
||||
},
|
||||
|
||||
// Hide scrollbar for Firefox
|
||||
scrollbarWidth: "none",
|
||||
scrollbarWidth: 'none',
|
||||
|
||||
// Hide scrollbar for IE and older Edge
|
||||
"-msOverflowStyle": "none",
|
||||
'-msOverflowStyle': 'none',
|
||||
});
|
||||
|
||||
export const AppsLibraryDesktop = ({
|
||||
@ -103,20 +113,20 @@ export const AppsLibraryDesktop = ({
|
||||
hasPublishApp,
|
||||
isShow,
|
||||
categories,
|
||||
getQapps
|
||||
getQapps,
|
||||
}) => {
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const virtuosoRef = useRef();
|
||||
|
||||
const officialApps = useMemo(() => {
|
||||
return availableQapps.filter(
|
||||
(app) =>
|
||||
app.service === "APP" &&
|
||||
app.service === 'APP' &&
|
||||
officialAppList.includes(app?.name?.toLowerCase())
|
||||
);
|
||||
}, [availableQapps]);
|
||||
|
||||
const [debouncedValue, setDebouncedValue] = useState(""); // Debounced value
|
||||
const [debouncedValue, setDebouncedValue] = useState(''); // Debounced value
|
||||
|
||||
// Debounce logic
|
||||
useEffect(() => {
|
||||
@ -125,7 +135,7 @@ export const AppsLibraryDesktop = ({
|
||||
}, 350);
|
||||
setTimeout(() => {
|
||||
virtuosoRef.current.scrollToIndex({
|
||||
index: 0
|
||||
index: 0,
|
||||
});
|
||||
}, 500);
|
||||
// Cleanup timeout if searchValue changes before the timeout completes
|
||||
@ -138,8 +148,13 @@ export const AppsLibraryDesktop = ({
|
||||
|
||||
const searchedList = useMemo(() => {
|
||||
if (!debouncedValue) return [];
|
||||
return availableQapps.filter((app) =>
|
||||
app.name.toLowerCase().includes(debouncedValue.toLowerCase()) || (app?.metadata?.title && app?.metadata?.title?.toLowerCase().includes(debouncedValue.toLowerCase()))
|
||||
return availableQapps.filter(
|
||||
(app) =>
|
||||
app.name.toLowerCase().includes(debouncedValue.toLowerCase()) ||
|
||||
(app?.metadata?.title &&
|
||||
app?.metadata?.title
|
||||
?.toLowerCase()
|
||||
.includes(debouncedValue.toLowerCase()))
|
||||
);
|
||||
}, [debouncedValue]);
|
||||
|
||||
@ -151,7 +166,7 @@ export const AppsLibraryDesktop = ({
|
||||
app={app}
|
||||
myName={myName}
|
||||
parentStyles={{
|
||||
padding: '0px 10px'
|
||||
padding: '0px 10px',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
@ -160,111 +175,112 @@ export const AppsLibraryDesktop = ({
|
||||
return (
|
||||
<AppsLibraryContainer
|
||||
sx={{
|
||||
display: !isShow && "none",
|
||||
padding: "0px",
|
||||
height: "100vh",
|
||||
overflow: "hidden",
|
||||
paddingTop: '30px'
|
||||
display: !isShow && 'none',
|
||||
padding: '0px',
|
||||
height: '100vh',
|
||||
overflow: 'hidden',
|
||||
paddingTop: '30px',
|
||||
}}
|
||||
>
|
||||
|
||||
<AppsDesktopLibraryHeader
|
||||
sx={{
|
||||
maxWidth: "1500px",
|
||||
width: "90%",
|
||||
maxWidth: '1500px',
|
||||
width: '90%',
|
||||
}}
|
||||
>
|
||||
<AppsWidthLimiter>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<img src={qappLibraryText} />
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
<AppsSearchContainer
|
||||
<Box
|
||||
sx={{
|
||||
width: "412px",
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<AppsSearchLeft>
|
||||
<img src={IconSearch} />
|
||||
<InputBase
|
||||
value={searchValue}
|
||||
onChange={(e) => setSearchValue(e.target.value)}
|
||||
sx={{ ml: 1, flex: 1 }}
|
||||
placeholder="Search for apps"
|
||||
inputProps={{
|
||||
"aria-label": "Search for apps",
|
||||
fontSize: "16px",
|
||||
fontWeight: 400,
|
||||
<AppsSearchContainer
|
||||
sx={{
|
||||
width: '412px',
|
||||
}}
|
||||
>
|
||||
<AppsSearchLeft>
|
||||
<img src={IconSearch} />
|
||||
<InputBase
|
||||
value={searchValue}
|
||||
onChange={(e) => setSearchValue(e.target.value)}
|
||||
sx={{ ml: 1, flex: 1 }}
|
||||
placeholder="Search for apps"
|
||||
inputProps={{
|
||||
'aria-label': 'Search for apps',
|
||||
fontSize: '16px',
|
||||
fontWeight: 400,
|
||||
}}
|
||||
/>
|
||||
</AppsSearchLeft>
|
||||
<AppsSearchRight>
|
||||
{searchValue && (
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setSearchValue('');
|
||||
}}
|
||||
>
|
||||
<img src={IconClearInput} />
|
||||
</ButtonBase>
|
||||
)}
|
||||
</AppsSearchRight>
|
||||
</AppsSearchContainer>
|
||||
<ButtonBase
|
||||
onClick={(e) => {
|
||||
getQapps();
|
||||
}}
|
||||
>
|
||||
<RefreshIcon
|
||||
sx={{
|
||||
color: 'rgba(250, 250, 250, 0.5)',
|
||||
width: '40px',
|
||||
height: 'auto',
|
||||
}}
|
||||
/>
|
||||
</AppsSearchLeft>
|
||||
<AppsSearchRight>
|
||||
{searchValue && (
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setSearchValue("");
|
||||
}}
|
||||
>
|
||||
<img src={IconClearInput} />
|
||||
</ButtonBase>
|
||||
)}
|
||||
</AppsSearchRight>
|
||||
</AppsSearchContainer>
|
||||
<ButtonBase
|
||||
onClick={(e) => {
|
||||
getQapps()
|
||||
}}
|
||||
>
|
||||
<RefreshIcon
|
||||
|
||||
sx={{
|
||||
color: "rgba(250, 250, 250, 0.5)",
|
||||
width: '40px',
|
||||
height: 'auto'
|
||||
}}
|
||||
/>
|
||||
</ButtonBase>
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
|
||||
</Box>
|
||||
</AppsWidthLimiter>
|
||||
</AppsDesktopLibraryHeader>
|
||||
<AppsDesktopLibraryBody
|
||||
sx={{
|
||||
height: `calc(100vh - 36px)`,
|
||||
overflow: "auto",
|
||||
padding: "0px",
|
||||
alignItems: "center",
|
||||
overflow: 'auto',
|
||||
padding: '0px',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<AppsDesktopLibraryBody
|
||||
sx={{
|
||||
height: `calc(100vh - 36px)`,
|
||||
flexGrow: "unset",
|
||||
maxWidth: "1500px",
|
||||
width: "90%",
|
||||
flexGrow: 'unset',
|
||||
maxWidth: '1500px',
|
||||
width: '90%',
|
||||
}}
|
||||
>
|
||||
|
||||
<Spacer height="70px" />
|
||||
<ShowMessageReturnButton sx={{
|
||||
padding: '2px'
|
||||
}} onClick={() => {
|
||||
executeEvent("navigateBack", {});
|
||||
}}>
|
||||
<MailIconImg src={ReturnSVG} />
|
||||
<ComposeP>Return to Apps Dashboard</ComposeP>
|
||||
</ShowMessageReturnButton>
|
||||
<Spacer height="20px" />
|
||||
<ShowMessageReturnButton
|
||||
sx={{
|
||||
padding: '2px',
|
||||
}}
|
||||
onClick={() => {
|
||||
executeEvent('navigateBack', {});
|
||||
}}
|
||||
>
|
||||
<MailIconImg src={ReturnSVG} />
|
||||
<ComposeP>Return to Apps Dashboard</ComposeP>
|
||||
</ShowMessageReturnButton>
|
||||
<Spacer height="20px" />
|
||||
{searchedList?.length > 0 ? (
|
||||
<AppsWidthLimiter>
|
||||
<StyledVirtuosoContainer
|
||||
@ -292,46 +308,48 @@ export const AppsLibraryDesktop = ({
|
||||
<>
|
||||
<AppLibrarySubTitle
|
||||
sx={{
|
||||
fontSize: "30px",
|
||||
fontSize: '30px',
|
||||
}}
|
||||
>
|
||||
Official Apps
|
||||
</AppLibrarySubTitle>
|
||||
<Spacer height="45px" />
|
||||
<AppsContainer sx={{
|
||||
gap: '50px',
|
||||
justifyContent: 'flex-start'
|
||||
}}>
|
||||
<AppsContainer
|
||||
sx={{
|
||||
gap: '50px',
|
||||
justifyContent: 'flex-start',
|
||||
}}
|
||||
>
|
||||
{officialApps?.map((qapp) => {
|
||||
return (
|
||||
<ButtonBase
|
||||
sx={{
|
||||
// height: "80px",
|
||||
width: "80px",
|
||||
width: '80px',
|
||||
}}
|
||||
onClick={() => {
|
||||
// executeEvent("addTab", {
|
||||
// data: qapp
|
||||
// })
|
||||
executeEvent("selectedAppInfo", {
|
||||
executeEvent('selectedAppInfo', {
|
||||
data: qapp,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<AppCircleContainer
|
||||
sx={{
|
||||
gap: "10px",
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<AppCircle
|
||||
sx={{
|
||||
border: "none",
|
||||
border: 'none',
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
sx={{
|
||||
height: "42px",
|
||||
width: "42px",
|
||||
height: '42px',
|
||||
width: '42px',
|
||||
}}
|
||||
alt={qapp?.name}
|
||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||
@ -340,8 +358,8 @@ export const AppsLibraryDesktop = ({
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: "31px",
|
||||
height: "auto",
|
||||
width: '31px',
|
||||
height: 'auto',
|
||||
}}
|
||||
src={LogoSelected}
|
||||
alt="center-icon"
|
||||
@ -359,30 +377,30 @@ export const AppsLibraryDesktop = ({
|
||||
<Spacer height="80px" />
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
gap: "250px",
|
||||
display: "flex",
|
||||
width: '100%',
|
||||
gap: '250px',
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<AppLibrarySubTitle
|
||||
sx={{
|
||||
fontSize: "30px",
|
||||
width: "100%",
|
||||
textAlign: "start",
|
||||
fontSize: '30px',
|
||||
width: '100%',
|
||||
textAlign: 'start',
|
||||
}}
|
||||
>
|
||||
{hasPublishApp ? "Update Apps!" : "Create Apps!"}
|
||||
{hasPublishApp ? 'Update Apps!' : 'Create Apps!'}
|
||||
</AppLibrarySubTitle>
|
||||
<Spacer height="18px" />
|
||||
<PublishQAppCTAParent
|
||||
sx={{
|
||||
gap: "25px",
|
||||
gap: '25px',
|
||||
}}
|
||||
>
|
||||
<PublishQAppCTALeft>
|
||||
@ -394,11 +412,11 @@ export const AppsLibraryDesktop = ({
|
||||
</PublishQAppCTALeft>
|
||||
<PublishQAppCTARight
|
||||
onClick={() => {
|
||||
setMode("publish");
|
||||
setMode('publish');
|
||||
}}
|
||||
>
|
||||
<PublishQAppCTAButton>
|
||||
{hasPublishApp ? "Update" : "Publish"}
|
||||
{hasPublishApp ? 'Update' : 'Publish'}
|
||||
</PublishQAppCTAButton>
|
||||
<Spacer width="20px" />
|
||||
</PublishQAppCTARight>
|
||||
@ -406,13 +424,13 @@ export const AppsLibraryDesktop = ({
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<AppLibrarySubTitle
|
||||
sx={{
|
||||
fontSize: "30px",
|
||||
fontSize: '30px',
|
||||
}}
|
||||
>
|
||||
Categories
|
||||
@ -420,58 +438,57 @@ export const AppsLibraryDesktop = ({
|
||||
<Spacer height="18px" />
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
flexWrap: "wrap",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
flexWrap: 'wrap',
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
|
||||
onClick={() => {
|
||||
executeEvent("selectedCategory", {
|
||||
data: {
|
||||
id: 'all',
|
||||
name: 'All'
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: "60px",
|
||||
padding: "0px 24px",
|
||||
border: "4px solid #10242F",
|
||||
borderRadius: "6px",
|
||||
boxShadow: "2px 4px 0px 0px #000000",
|
||||
}}
|
||||
>
|
||||
All
|
||||
</Box>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
executeEvent('selectedCategory', {
|
||||
data: {
|
||||
id: 'all',
|
||||
name: 'All',
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: '60px',
|
||||
padding: '0px 24px',
|
||||
border: '4px solid #10242F',
|
||||
borderRadius: '6px',
|
||||
boxShadow: '2px 4px 0px 0px #000000',
|
||||
}}
|
||||
>
|
||||
All
|
||||
</Box>
|
||||
</ButtonBase>
|
||||
{categories?.map((category) => {
|
||||
return (
|
||||
<ButtonBase
|
||||
key={category?.id}
|
||||
onClick={() => {
|
||||
executeEvent("selectedCategory", {
|
||||
executeEvent('selectedCategory', {
|
||||
data: category,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: "60px",
|
||||
padding: "0px 24px",
|
||||
border: "4px solid #10242F",
|
||||
borderRadius: "6px",
|
||||
boxShadow: "2px 4px 0px 0px #000000",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: '60px',
|
||||
padding: '0px 24px',
|
||||
border: '4px solid #10242F',
|
||||
borderRadius: '6px',
|
||||
boxShadow: '2px 4px 0px 0px #000000',
|
||||
}}
|
||||
>
|
||||
{category?.name}
|
||||
|
@ -1,18 +1,32 @@
|
||||
import React, { useMemo, useRef, useState } from "react";
|
||||
import TipTap from "./TipTap";
|
||||
import { AuthenticatedContainerInnerTop, CustomButton } from "../../App-styles";
|
||||
import { Box, CircularProgress } from "@mui/material";
|
||||
import { objectToBase64 } from "../../qdn/encryption/group-encryption";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar";
|
||||
import { getBaseApi, getFee } from "../../background";
|
||||
import { decryptPublishes, getTempPublish, handleUnencryptedPublishes, saveTempPublish } from "./GroupAnnouncements";
|
||||
import { AnnouncementList } from "./AnnouncementList";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import React, { useMemo, useRef, useState } from 'react';
|
||||
import TipTap from './TipTap';
|
||||
import {
|
||||
AuthenticatedContainerInnerTop,
|
||||
CustomButton,
|
||||
} from '../../styles/App-styles';
|
||||
import { Box, CircularProgress } from '@mui/material';
|
||||
import { objectToBase64 } from '../../qdn/encryption/group-encryption';
|
||||
import ShortUniqueId from 'short-unique-id';
|
||||
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
||||
import { getBaseApi, getFee } from '../../background';
|
||||
import {
|
||||
decryptPublishes,
|
||||
getTempPublish,
|
||||
handleUnencryptedPublishes,
|
||||
saveTempPublish,
|
||||
} from './GroupAnnouncements';
|
||||
import { AnnouncementList } from './AnnouncementList';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import { getArbitraryEndpointReact, getBaseApiReact, isMobile, pauseAllQueues, resumeAllQueues } from "../../App";
|
||||
import {
|
||||
getArbitraryEndpointReact,
|
||||
getBaseApiReact,
|
||||
isMobile,
|
||||
pauseAllQueues,
|
||||
resumeAllQueues,
|
||||
} from '../../App';
|
||||
|
||||
const tempKey = 'accouncement-comment'
|
||||
const tempKey = 'accouncement-comment';
|
||||
|
||||
const uid = new ShortUniqueId({ length: 8 });
|
||||
export const AnnouncementDiscussion = ({
|
||||
@ -23,28 +37,28 @@ export const AnnouncementDiscussion = ({
|
||||
setSelectedAnnouncement,
|
||||
show,
|
||||
myName,
|
||||
isPrivate
|
||||
isPrivate,
|
||||
}) => {
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isFocusedParent, setIsFocusedParent] = useState(false);
|
||||
|
||||
const [comments, setComments] = useState([])
|
||||
const [tempPublishedList, setTempPublishedList] = useState([])
|
||||
const firstMountRef = useRef(false)
|
||||
const [data, setData] = useState({})
|
||||
const [comments, setComments] = useState([]);
|
||||
const [tempPublishedList, setTempPublishedList] = useState([]);
|
||||
const firstMountRef = useRef(false);
|
||||
const [data, setData] = useState({});
|
||||
const editorRef = useRef(null);
|
||||
const setEditorRef = (editorInstance) => {
|
||||
editorRef.current = editorInstance;
|
||||
};
|
||||
|
||||
|
||||
const clearEditorContent = () => {
|
||||
if (editorRef.current) {
|
||||
editorRef.current.chain().focus().clearContent().run();
|
||||
if(isMobile){
|
||||
if (isMobile) {
|
||||
setTimeout(() => {
|
||||
editorRef.current?.chain().blur().run();
|
||||
setIsFocusedParent(false)
|
||||
editorRef.current?.chain().blur().run();
|
||||
setIsFocusedParent(false);
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
@ -52,14 +66,16 @@ export const AnnouncementDiscussion = ({
|
||||
|
||||
const getData = async ({ identifier, name }, isPrivate) => {
|
||||
try {
|
||||
|
||||
const res = await fetch(
|
||||
`${getBaseApiReact()}/arbitrary/DOCUMENT/${name}/${identifier}?encoding=base64`
|
||||
);
|
||||
if(!res?.ok) return
|
||||
if (!res?.ok) return;
|
||||
const data = await res.text();
|
||||
const response = isPrivate === false ? handleUnencryptedPublishes([data]) : await decryptPublishes([{ data }], secretKey);
|
||||
|
||||
const response =
|
||||
isPrivate === false
|
||||
? handleUnencryptedPublishes([data])
|
||||
: await decryptPublishes([{ data }], secretKey);
|
||||
|
||||
const messageData = response[0];
|
||||
setData((prev) => {
|
||||
return {
|
||||
@ -67,19 +83,19 @@ export const AnnouncementDiscussion = ({
|
||||
[`${identifier}-${name}`]: messageData,
|
||||
};
|
||||
});
|
||||
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const publishAnc = async ({ encryptedData, identifier }: any) => {
|
||||
try {
|
||||
if (!selectedAnnouncement) return;
|
||||
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
window.sendMessage("publishGroupEncryptedResource", {
|
||||
encryptedData,
|
||||
identifier,
|
||||
})
|
||||
window
|
||||
.sendMessage('publishGroupEncryptedResource', {
|
||||
encryptedData,
|
||||
identifier,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
res(response);
|
||||
@ -88,63 +104,60 @@ export const AnnouncementDiscussion = ({
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || "An error occurred");
|
||||
rej(error.message || 'An error occurred');
|
||||
});
|
||||
|
||||
});
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const setTempData = async ()=> {
|
||||
const setTempData = async () => {
|
||||
try {
|
||||
const getTempAnnouncements = await getTempPublish()
|
||||
if(getTempAnnouncements[tempKey]){
|
||||
let tempData = []
|
||||
Object.keys(getTempAnnouncements[tempKey] || {}).map((key)=> {
|
||||
const value = getTempAnnouncements[tempKey][key]
|
||||
if(value.data?.announcementId === selectedAnnouncement.identifier){
|
||||
tempData.push(value.data)
|
||||
const getTempAnnouncements = await getTempPublish();
|
||||
if (getTempAnnouncements[tempKey]) {
|
||||
let tempData = [];
|
||||
Object.keys(getTempAnnouncements[tempKey] || {}).map((key) => {
|
||||
const value = getTempAnnouncements[tempKey][key];
|
||||
if (value.data?.announcementId === selectedAnnouncement.identifier) {
|
||||
tempData.push(value.data);
|
||||
}
|
||||
});
|
||||
setTempPublishedList(tempData);
|
||||
}
|
||||
})
|
||||
setTempPublishedList(tempData)
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const publishComment = async () => {
|
||||
try {
|
||||
pauseAllQueues()
|
||||
const fee = await getFee('ARBITRARY')
|
||||
pauseAllQueues();
|
||||
const fee = await getFee('ARBITRARY');
|
||||
await show({
|
||||
message: "Would you like to perform a ARBITRARY transaction?" ,
|
||||
publishFee: fee.fee + ' QORT'
|
||||
})
|
||||
message: 'Would you like to perform a ARBITRARY transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
if (isSending) return;
|
||||
if (editorRef.current) {
|
||||
const htmlContent = editorRef.current.getHTML();
|
||||
|
||||
if (!htmlContent?.trim() || htmlContent?.trim() === "<p></p>") return;
|
||||
|
||||
if (!htmlContent?.trim() || htmlContent?.trim() === '<p></p>') return;
|
||||
setIsSending(true);
|
||||
const message = {
|
||||
version: 1,
|
||||
extra: {},
|
||||
message: htmlContent,
|
||||
};
|
||||
const secretKeyObject = isPrivate === false ? null : await getSecretKey(false, true);
|
||||
const message64: any = await objectToBase64(message);
|
||||
|
||||
const encryptSingle = isPrivate === false ? message64 : await encryptChatMessage(
|
||||
message64,
|
||||
secretKeyObject
|
||||
);
|
||||
const secretKeyObject =
|
||||
isPrivate === false ? null : await getSecretKey(false, true);
|
||||
const message64: any = await objectToBase64(message);
|
||||
|
||||
const encryptSingle =
|
||||
isPrivate === false
|
||||
? message64
|
||||
: await encryptChatMessage(message64, secretKeyObject);
|
||||
const randomUid = uid.rnd();
|
||||
const identifier = `cm-${selectedAnnouncement.identifier}-${randomUid}`;
|
||||
const res = await publishAnc({
|
||||
encryptedData: encryptSingle,
|
||||
identifier
|
||||
identifier,
|
||||
});
|
||||
|
||||
const dataToSaveToStorage = {
|
||||
@ -153,18 +166,18 @@ export const AnnouncementDiscussion = ({
|
||||
service: 'DOCUMENT',
|
||||
tempData: message,
|
||||
created: Date.now(),
|
||||
announcementId: selectedAnnouncement.identifier
|
||||
}
|
||||
await saveTempPublish({data: dataToSaveToStorage, key: tempKey})
|
||||
setTempData()
|
||||
|
||||
announcementId: selectedAnnouncement.identifier,
|
||||
};
|
||||
await saveTempPublish({ data: dataToSaveToStorage, key: tempKey });
|
||||
setTempData();
|
||||
|
||||
clearEditorContent();
|
||||
}
|
||||
// send chat message
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
resumeAllQueues()
|
||||
resumeAllQueues();
|
||||
setIsSending(false);
|
||||
}
|
||||
};
|
||||
@ -172,7 +185,6 @@ export const AnnouncementDiscussion = ({
|
||||
const getComments = React.useCallback(
|
||||
async (selectedAnnouncement, isPrivate) => {
|
||||
try {
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
const offset = 0;
|
||||
@ -181,13 +193,13 @@ export const AnnouncementDiscussion = ({
|
||||
const identifier = `cm-${selectedAnnouncement.identifier}`;
|
||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const responseData = await response.json();
|
||||
setTempData()
|
||||
setTempData();
|
||||
setComments(responseData);
|
||||
setIsLoading(false);
|
||||
for (const data of responseData) {
|
||||
@ -203,119 +215,122 @@ export const AnnouncementDiscussion = ({
|
||||
[secretKey]
|
||||
);
|
||||
|
||||
const loadMore = async()=> {
|
||||
const loadMore = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
const offset = comments.length
|
||||
const offset = comments.length;
|
||||
const identifier = `cm-${selectedAnnouncement.identifier}`;
|
||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const responseData = await response.json();
|
||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`;
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const responseData = await response.json();
|
||||
|
||||
setComments((prev)=> [...prev, ...responseData]);
|
||||
setIsLoading(false);
|
||||
for (const data of responseData) {
|
||||
getData({ name: data.name, identifier: data.identifier }, isPrivate);
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
setComments((prev) => [...prev, ...responseData]);
|
||||
setIsLoading(false);
|
||||
for (const data of responseData) {
|
||||
getData({ name: data.name, identifier: data.identifier }, isPrivate);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const combinedListTempAndReal = useMemo(() => {
|
||||
// Combine the two lists
|
||||
const combined = [...tempPublishedList, ...comments];
|
||||
|
||||
|
||||
// Remove duplicates based on the "identifier"
|
||||
const uniqueItems = new Map();
|
||||
combined.forEach(item => {
|
||||
uniqueItems.set(item.identifier, item); // This will overwrite duplicates, keeping the last occurrence
|
||||
combined.forEach((item) => {
|
||||
uniqueItems.set(item.identifier, item); // This will overwrite duplicates, keeping the last occurrence
|
||||
});
|
||||
|
||||
|
||||
// Convert the map back to an array and sort by "created" timestamp in descending order
|
||||
const sortedList = Array.from(uniqueItems.values()).sort((a, b) => b.created - a.created);
|
||||
|
||||
const sortedList = Array.from(uniqueItems.values()).sort(
|
||||
(a, b) => b.created - a.created
|
||||
);
|
||||
|
||||
return sortedList;
|
||||
}, [tempPublishedList, comments]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if(!secretKey && isPrivate) return
|
||||
if (!secretKey && isPrivate) return;
|
||||
if (selectedAnnouncement && !firstMountRef.current && isPrivate !== null) {
|
||||
getComments(selectedAnnouncement, isPrivate);
|
||||
firstMountRef.current = true
|
||||
firstMountRef.current = true;
|
||||
}
|
||||
}, [selectedAnnouncement, secretKey, isPrivate]);
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
height: isMobile ? '100%' : "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
height: isMobile ? '100%' : '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexShrink: 0,
|
||||
}}>
|
||||
|
||||
<AuthenticatedContainerInnerTop sx={{
|
||||
height: '20px'
|
||||
}}>
|
||||
<ArrowBackIcon onClick={()=> setSelectedAnnouncement(null)} sx={{
|
||||
cursor: 'pointer'
|
||||
}} />
|
||||
</AuthenticatedContainerInnerTop>
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<AuthenticatedContainerInnerTop
|
||||
sx={{
|
||||
height: '20px',
|
||||
}}
|
||||
>
|
||||
<ArrowBackIcon
|
||||
onClick={() => setSelectedAnnouncement(null)}
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
/>
|
||||
</AuthenticatedContainerInnerTop>
|
||||
<Spacer height="20px" />
|
||||
|
||||
</div>
|
||||
<AnnouncementList
|
||||
announcementData={data}
|
||||
initialMessages={combinedListTempAndReal}
|
||||
setSelectedAnnouncement={()=> {}}
|
||||
setSelectedAnnouncement={() => {}}
|
||||
disableComment
|
||||
showLoadMore={comments.length > 0 && comments.length % 20 === 0}
|
||||
loadMore={loadMore}
|
||||
myName={myName}
|
||||
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
// position: 'fixed',
|
||||
// bottom: '0px',
|
||||
backgroundColor: "#232428",
|
||||
minHeight: isMobile ? "0px" : "150px",
|
||||
maxHeight: isMobile ? "auto" : "400px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflow: "hidden",
|
||||
width: "100%",
|
||||
boxSizing: "border-box",
|
||||
padding: isMobile ? "10px": "20px",
|
||||
backgroundColor: '#232428',
|
||||
minHeight: isMobile ? '0px' : '150px',
|
||||
maxHeight: isMobile ? 'auto' : '400px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
width: '100%',
|
||||
boxSizing: 'border-box',
|
||||
padding: isMobile ? '10px' : '20px',
|
||||
position: isFocusedParent ? 'fixed' : 'relative',
|
||||
bottom: isFocusedParent ? '0px' : 'unset',
|
||||
top: isFocusedParent ? '0px' : 'unset',
|
||||
zIndex: isFocusedParent ? 5 : 'unset',
|
||||
flexShrink:0,
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
// height: '100%',
|
||||
flexGrow: isMobile && 1,
|
||||
overflow: "auto",
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<TipTap
|
||||
@ -323,79 +338,78 @@ export const AnnouncementDiscussion = ({
|
||||
onEnter={publishComment}
|
||||
disableEnter
|
||||
maxHeightOffset="60px"
|
||||
isFocusedParent={isFocusedParent} setIsFocusedParent={setIsFocusedParent}
|
||||
|
||||
isFocusedParent={isFocusedParent}
|
||||
setIsFocusedParent={setIsFocusedParent}
|
||||
/>
|
||||
</div>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
width: '100&',
|
||||
gap: '10px',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0,
|
||||
position: 'relative',
|
||||
}}>
|
||||
{isFocusedParent && (
|
||||
<CustomButton
|
||||
onClick={()=> {
|
||||
if(isSending) return
|
||||
setIsFocusedParent(false)
|
||||
clearEditorContent()
|
||||
// Unfocus the editor
|
||||
}}
|
||||
style={{
|
||||
marginTop: 'auto',
|
||||
alignSelf: 'center',
|
||||
cursor: isSending ? 'default' : 'pointer',
|
||||
flexShrink: 0,
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
background: 'red',
|
||||
}}
|
||||
>
|
||||
|
||||
{` Close`}
|
||||
</CustomButton>
|
||||
|
||||
)}
|
||||
<CustomButton
|
||||
onClick={() => {
|
||||
if (isSending) return;
|
||||
publishComment();
|
||||
}}
|
||||
style={{
|
||||
marginTop: "auto",
|
||||
alignSelf: "center",
|
||||
cursor: isSending ? "default" : "pointer",
|
||||
background: isSending && "rgba(0, 0, 0, 0.8)",
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
width: '100&',
|
||||
gap: '10px',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0,
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px'
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{isSending && (
|
||||
<CircularProgress
|
||||
size={18}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
marginTop: "-12px",
|
||||
marginLeft: "-12px",
|
||||
color: "white",
|
||||
{isFocusedParent && (
|
||||
<CustomButton
|
||||
onClick={() => {
|
||||
if (isSending) return;
|
||||
setIsFocusedParent(false);
|
||||
clearEditorContent();
|
||||
// Unfocus the editor
|
||||
}}
|
||||
/>
|
||||
style={{
|
||||
marginTop: 'auto',
|
||||
alignSelf: 'center',
|
||||
cursor: isSending ? 'default' : 'pointer',
|
||||
flexShrink: 0,
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
background: 'red',
|
||||
}}
|
||||
>
|
||||
{` Close`}
|
||||
</CustomButton>
|
||||
)}
|
||||
{` Publish Comment`}
|
||||
</CustomButton>
|
||||
|
||||
</Box>
|
||||
<CustomButton
|
||||
onClick={() => {
|
||||
if (isSending) return;
|
||||
publishComment();
|
||||
}}
|
||||
style={{
|
||||
marginTop: 'auto',
|
||||
alignSelf: 'center',
|
||||
cursor: isSending ? 'default' : 'pointer',
|
||||
background: isSending && 'rgba(0, 0, 0, 0.8)',
|
||||
flexShrink: 0,
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
}}
|
||||
>
|
||||
{isSending && (
|
||||
<CircularProgress
|
||||
size={18}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
marginTop: '-12px',
|
||||
marginLeft: '-12px',
|
||||
color: 'white',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{` Publish Comment`}
|
||||
</CustomButton>
|
||||
</Box>
|
||||
</div>
|
||||
|
||||
|
||||
<LoadingSnackbar
|
||||
open={isLoading}
|
||||
info={{
|
||||
message: "Loading comments... please wait.",
|
||||
message: 'Loading comments... please wait.',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -1,13 +1,13 @@
|
||||
import React, { useCallback, useState, useEffect, useRef } from "react";
|
||||
import React, { useCallback, useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
List,
|
||||
AutoSizer,
|
||||
CellMeasurerCache,
|
||||
CellMeasurer,
|
||||
} from "react-virtualized";
|
||||
import { AnnouncementItem } from "./AnnouncementItem";
|
||||
import { Box } from "@mui/material";
|
||||
import { CustomButton } from "../../App-styles";
|
||||
} from 'react-virtualized';
|
||||
import { AnnouncementItem } from './AnnouncementItem';
|
||||
import { Box } from '@mui/material';
|
||||
import { CustomButton } from '../../styles/App-styles';
|
||||
|
||||
const cache = new CellMeasurerCache({
|
||||
fixedWidth: true,
|
||||
@ -21,9 +21,8 @@ export const AnnouncementList = ({
|
||||
disableComment,
|
||||
showLoadMore,
|
||||
loadMore,
|
||||
myName
|
||||
myName,
|
||||
}) => {
|
||||
|
||||
const listRef = useRef();
|
||||
const [messages, setMessages] = useState(initialMessages);
|
||||
|
||||
@ -35,39 +34,44 @@ export const AnnouncementList = ({
|
||||
setMessages(initialMessages);
|
||||
}, [initialMessages]);
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: "relative",
|
||||
position: 'relative',
|
||||
flexGrow: 1,
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 1,
|
||||
overflow: 'auto'
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
{messages.map((message) => {
|
||||
const messageData = message?.tempData ? {
|
||||
decryptedData: message?.tempData
|
||||
} : announcementData[`${message.identifier}-${message.name}`];
|
||||
const messageData = message?.tempData
|
||||
? {
|
||||
decryptedData: message?.tempData,
|
||||
}
|
||||
: announcementData[`${message.identifier}-${message.name}`];
|
||||
|
||||
return (
|
||||
|
||||
<div
|
||||
key={message?.identifier}
|
||||
style={{
|
||||
marginBottom: "10px",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<AnnouncementItem myName={myName} disableComment={disableComment} setSelectedAnnouncement={setSelectedAnnouncement} message={message} messageData={messageData} />
|
||||
</div>
|
||||
|
||||
<div
|
||||
key={message?.identifier}
|
||||
style={{
|
||||
marginBottom: '10px',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<AnnouncementItem
|
||||
myName={myName}
|
||||
disableComment={disableComment}
|
||||
setSelectedAnnouncement={setSelectedAnnouncement}
|
||||
message={message}
|
||||
messageData={messageData}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{/* <AutoSizer>
|
||||
@ -83,16 +87,20 @@ export const AnnouncementList = ({
|
||||
/>
|
||||
)}
|
||||
</AutoSizer> */}
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
marginTop: '25px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
{showLoadMore && (
|
||||
<CustomButton onClick={loadMore}>Load older announcements</CustomButton>
|
||||
)}
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
marginTop: '25px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{showLoadMore && (
|
||||
<CustomButton onClick={loadMore}>
|
||||
Load older announcements
|
||||
</CustomButton>
|
||||
)}
|
||||
</Box>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -4,30 +4,33 @@ import React, {
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { CreateCommonSecret } from "./CreateCommonSecret";
|
||||
import { reusableGet } from "../../qdn/publish/pubish";
|
||||
import { uint8ArrayToObject } from "../../backgroundFunctions/encryption";
|
||||
} from 'react';
|
||||
import { CreateCommonSecret } from './CreateCommonSecret';
|
||||
import { reusableGet } from '../../qdn/publish/pubish';
|
||||
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
|
||||
import {
|
||||
base64ToUint8Array,
|
||||
objectToBase64,
|
||||
} from "../../qdn/encryption/group-encryption";
|
||||
import { ChatContainerComp } from "./ChatContainer";
|
||||
import { ChatList } from "./ChatList";
|
||||
import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
|
||||
import Tiptap from "./TipTap";
|
||||
import { AuthenticatedContainerInnerTop, CustomButton } from "../../App-styles";
|
||||
import CircularProgress from "@mui/material/CircularProgress";
|
||||
import { getBaseApi, getFee } from "../../background";
|
||||
import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import { AnnouncementList } from "./AnnouncementList";
|
||||
} from '../../qdn/encryption/group-encryption';
|
||||
import { ChatContainerComp } from './ChatContainer';
|
||||
import { ChatList } from './ChatList';
|
||||
import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css';
|
||||
import Tiptap from './TipTap';
|
||||
import {
|
||||
AuthenticatedContainerInnerTop,
|
||||
CustomButton,
|
||||
} from '../../styles/App-styles';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import { getBaseApi, getFee } from '../../background';
|
||||
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import ShortUniqueId from 'short-unique-id';
|
||||
import { AnnouncementList } from './AnnouncementList';
|
||||
const uid = new ShortUniqueId({ length: 8 });
|
||||
import CampaignIcon from "@mui/icons-material/Campaign";
|
||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||
import { AnnouncementDiscussion } from "./AnnouncementDiscussion";
|
||||
import CampaignIcon from '@mui/icons-material/Campaign';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import { AnnouncementDiscussion } from './AnnouncementDiscussion';
|
||||
import {
|
||||
MyContext,
|
||||
getArbitraryEndpointReact,
|
||||
@ -35,11 +38,11 @@ import {
|
||||
isMobile,
|
||||
pauseAllQueues,
|
||||
resumeAllQueues,
|
||||
} from "../../App";
|
||||
import { RequestQueueWithPromise } from "../../utils/queue/queue";
|
||||
import { CustomizedSnackbars } from "../Snackbar/Snackbar";
|
||||
import { addDataPublishesFunc, getDataPublishesFunc } from "../Group/Group";
|
||||
import { getRootHeight } from "../../utils/mobile/mobileUtils";
|
||||
} from '../../App';
|
||||
import { RequestQueueWithPromise } from '../../utils/queue/queue';
|
||||
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||
import { addDataPublishesFunc, getDataPublishesFunc } from '../Group/Group';
|
||||
import { getRootHeight } from '../../utils/mobile/mobileUtils';
|
||||
|
||||
export const requestQueueCommentCount = new RequestQueueWithPromise(3);
|
||||
export const requestQueuePublishedAccouncements = new RequestQueueWithPromise(
|
||||
@ -48,10 +51,11 @@ export const requestQueuePublishedAccouncements = new RequestQueueWithPromise(
|
||||
|
||||
export const saveTempPublish = async ({ data, key }: any) => {
|
||||
return new Promise((res, rej) => {
|
||||
window.sendMessage("saveTempPublish", {
|
||||
data,
|
||||
key,
|
||||
})
|
||||
window
|
||||
.sendMessage('saveTempPublish', {
|
||||
data,
|
||||
key,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
res(response);
|
||||
@ -60,37 +64,37 @@ export const saveTempPublish = async ({ data, key }: any) => {
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || "An error occurred");
|
||||
rej(error.message || 'An error occurred');
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
export const getTempPublish = async () => {
|
||||
return new Promise((res, rej) => {
|
||||
window.sendMessage("getTempPublish", {})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
res(response);
|
||||
return;
|
||||
}
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || "An error occurred");
|
||||
});
|
||||
|
||||
window
|
||||
.sendMessage('getTempPublish', {})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
res(response);
|
||||
return;
|
||||
}
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || 'An error occurred');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const decryptPublishes = async (encryptedMessages: any[], secretKey) => {
|
||||
try {
|
||||
return await new Promise((res, rej) => {
|
||||
window.sendMessage("decryptSingleForPublishes", {
|
||||
data: encryptedMessages,
|
||||
secretKeyObject: secretKey,
|
||||
skipDecodeBase64: true,
|
||||
})
|
||||
window
|
||||
.sendMessage('decryptSingleForPublishes', {
|
||||
data: encryptedMessages,
|
||||
secretKeyObject: secretKey,
|
||||
skipDecodeBase64: true,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
res(response);
|
||||
@ -99,26 +103,23 @@ export const decryptPublishes = async (encryptedMessages: any[], secretKey) => {
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || "An error occurred");
|
||||
rej(error.message || 'An error occurred');
|
||||
});
|
||||
|
||||
});
|
||||
} catch (error) {}
|
||||
};
|
||||
export const handleUnencryptedPublishes = (publishes) => {
|
||||
let publishesData = []
|
||||
publishes.forEach((pub)=> {
|
||||
export const handleUnencryptedPublishes = (publishes) => {
|
||||
let publishesData = [];
|
||||
publishes.forEach((pub) => {
|
||||
try {
|
||||
const decryptToUnit8Array = base64ToUint8Array(pub);
|
||||
const decodedData = uint8ArrayToObject(decryptToUnit8Array);
|
||||
if(decodedData){
|
||||
publishesData.push({decryptedData: decodedData})
|
||||
if (decodedData) {
|
||||
publishesData.push({ decryptedData: decodedData });
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
})
|
||||
return publishesData
|
||||
} catch (error) {}
|
||||
});
|
||||
return publishesData;
|
||||
};
|
||||
export const GroupAnnouncements = ({
|
||||
selectedGroup,
|
||||
@ -130,7 +131,7 @@ export const GroupAnnouncements = ({
|
||||
isAdmin,
|
||||
hide,
|
||||
myName,
|
||||
isPrivate
|
||||
isPrivate,
|
||||
}) => {
|
||||
const [messages, setMessages] = useState([]);
|
||||
const [isSending, setIsSending] = useState(false);
|
||||
@ -159,12 +160,15 @@ export const GroupAnnouncements = ({
|
||||
useEffect(() => {
|
||||
if (!selectedGroup) return;
|
||||
(async () => {
|
||||
const res = await getDataPublishesFunc(selectedGroup, "anc");
|
||||
const res = await getDataPublishesFunc(selectedGroup, 'anc');
|
||||
dataPublishes.current = res || {};
|
||||
})();
|
||||
}, [selectedGroup]);
|
||||
|
||||
const getAnnouncementData = async ({ identifier, name, resource }, isPrivate) => {
|
||||
const getAnnouncementData = async (
|
||||
{ identifier, name, resource },
|
||||
isPrivate
|
||||
) => {
|
||||
try {
|
||||
let data = dataPublishes.current[`${name}-${identifier}`];
|
||||
if (
|
||||
@ -179,14 +183,17 @@ export const GroupAnnouncements = ({
|
||||
});
|
||||
if (!res?.ok) return;
|
||||
data = await res.text();
|
||||
await addDataPublishesFunc({ ...resource, data }, selectedGroup, "anc");
|
||||
await addDataPublishesFunc({ ...resource, data }, selectedGroup, 'anc');
|
||||
} else {
|
||||
data = data.data;
|
||||
}
|
||||
|
||||
const response = isPrivate === false ? handleUnencryptedPublishes([data]) : await decryptPublishes([{ data }], secretKey);
|
||||
const response =
|
||||
isPrivate === false
|
||||
? handleUnencryptedPublishes([data])
|
||||
: await decryptPublishes([{ data }], secretKey);
|
||||
const messageData = response[0];
|
||||
if(!messageData) return
|
||||
if (!messageData) return;
|
||||
setAnnouncementData((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
@ -194,12 +201,17 @@ export const GroupAnnouncements = ({
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("error", error);
|
||||
console.error('error', error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if ((!secretKey && isPrivate) || hasInitializedWebsocket.current || isPrivate === null) return;
|
||||
if (
|
||||
(!secretKey && isPrivate) ||
|
||||
hasInitializedWebsocket.current ||
|
||||
isPrivate === null
|
||||
)
|
||||
return;
|
||||
setIsLoading(true);
|
||||
// initWebsocketMessageGroup()
|
||||
hasInitializedWebsocket.current = true;
|
||||
@ -208,10 +220,11 @@ export const GroupAnnouncements = ({
|
||||
const encryptChatMessage = async (data: string, secretKeyObject: any) => {
|
||||
try {
|
||||
return new Promise((res, rej) => {
|
||||
window.sendMessage("encryptSingle", {
|
||||
data,
|
||||
secretKeyObject,
|
||||
})
|
||||
window
|
||||
.sendMessage('encryptSingle', {
|
||||
data,
|
||||
secretKeyObject,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
res(response);
|
||||
@ -220,19 +233,19 @@ export const GroupAnnouncements = ({
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || "An error occurred");
|
||||
rej(error.message || 'An error occurred');
|
||||
});
|
||||
|
||||
});
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const publishAnc = async ({ encryptedData, identifier }: any) => {
|
||||
return new Promise((res, rej) => {
|
||||
window.sendMessage("publishGroupEncryptedResource", {
|
||||
encryptedData,
|
||||
identifier,
|
||||
})
|
||||
window
|
||||
.sendMessage('publishGroupEncryptedResource', {
|
||||
encryptedData,
|
||||
identifier,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
res(response);
|
||||
@ -241,9 +254,8 @@ export const GroupAnnouncements = ({
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || "An error occurred");
|
||||
rej(error.message || 'An error occurred');
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
const clearEditorContent = () => {
|
||||
@ -255,7 +267,7 @@ export const GroupAnnouncements = ({
|
||||
setIsFocusedParent(false);
|
||||
setTimeout(() => {
|
||||
triggerRerender();
|
||||
}, 300);
|
||||
}, 300);
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
@ -266,10 +278,12 @@ export const GroupAnnouncements = ({
|
||||
const getTempAnnouncements = await getTempPublish();
|
||||
if (getTempAnnouncements?.announcement) {
|
||||
let tempData = [];
|
||||
Object.keys(getTempAnnouncements?.announcement || {}).filter((annKey)=> annKey?.startsWith(`grp-${selectedGroup}-anc`)).map((key) => {
|
||||
const value = getTempAnnouncements?.announcement[key];
|
||||
tempData.push(value.data);
|
||||
});
|
||||
Object.keys(getTempAnnouncements?.announcement || {})
|
||||
.filter((annKey) => annKey?.startsWith(`grp-${selectedGroup}-anc`))
|
||||
.map((key) => {
|
||||
const value = getTempAnnouncements?.announcement[key];
|
||||
tempData.push(value.data);
|
||||
});
|
||||
setTempPublishedList(tempData);
|
||||
}
|
||||
} catch (error) {}
|
||||
@ -278,27 +292,28 @@ export const GroupAnnouncements = ({
|
||||
const publishAnnouncement = async () => {
|
||||
try {
|
||||
pauseAllQueues();
|
||||
const fee = await getFee("ARBITRARY");
|
||||
const fee = await getFee('ARBITRARY');
|
||||
await show({
|
||||
message: "Would you like to perform a ARBITRARY transaction?",
|
||||
publishFee: fee.fee + " QORT",
|
||||
message: 'Would you like to perform a ARBITRARY transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
if (isSending) return;
|
||||
if (editorRef.current) {
|
||||
const htmlContent = editorRef.current.getHTML();
|
||||
if (!htmlContent?.trim() || htmlContent?.trim() === "<p></p>") return;
|
||||
if (!htmlContent?.trim() || htmlContent?.trim() === '<p></p>') return;
|
||||
setIsSending(true);
|
||||
const message = {
|
||||
version: 1,
|
||||
extra: {},
|
||||
message: htmlContent,
|
||||
};
|
||||
const secretKeyObject = isPrivate === false ? null : await getSecretKey(false, true);
|
||||
const message64: any = await objectToBase64(message);
|
||||
const encryptSingle = isPrivate === false ? message64 : await encryptChatMessage(
|
||||
message64,
|
||||
secretKeyObject
|
||||
);
|
||||
const secretKeyObject =
|
||||
isPrivate === false ? null : await getSecretKey(false, true);
|
||||
const message64: any = await objectToBase64(message);
|
||||
const encryptSingle =
|
||||
isPrivate === false
|
||||
? message64
|
||||
: await encryptChatMessage(message64, secretKeyObject);
|
||||
const randomUid = uid.rnd();
|
||||
const identifier = `grp-${selectedGroup}-anc-${randomUid}`;
|
||||
const res = await publishAnc({
|
||||
@ -309,13 +324,13 @@ export const GroupAnnouncements = ({
|
||||
const dataToSaveToStorage = {
|
||||
name: myName,
|
||||
identifier,
|
||||
service: "DOCUMENT",
|
||||
service: 'DOCUMENT',
|
||||
tempData: message,
|
||||
created: Date.now(),
|
||||
};
|
||||
await saveTempPublish({
|
||||
data: dataToSaveToStorage,
|
||||
key: "announcement",
|
||||
key: 'announcement',
|
||||
});
|
||||
setTempData(selectedGroup);
|
||||
clearEditorContent();
|
||||
@ -324,7 +339,7 @@ export const GroupAnnouncements = ({
|
||||
} catch (error) {
|
||||
if (!error) return;
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
type: 'error',
|
||||
message: error,
|
||||
});
|
||||
setOpenSnack(true);
|
||||
@ -343,9 +358,9 @@ export const GroupAnnouncements = ({
|
||||
const identifier = `grp-${selectedGroup}-anc-`;
|
||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const responseData = await response.json();
|
||||
@ -354,11 +369,14 @@ export const GroupAnnouncements = ({
|
||||
setAnnouncements(responseData);
|
||||
setIsLoading(false);
|
||||
for (const data of responseData) {
|
||||
getAnnouncementData({
|
||||
name: data.name,
|
||||
identifier: data.identifier,
|
||||
resource: data,
|
||||
}, isPrivate);
|
||||
getAnnouncementData(
|
||||
{
|
||||
name: data.name,
|
||||
identifier: data.identifier,
|
||||
resource: data,
|
||||
},
|
||||
isPrivate
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
} finally {
|
||||
@ -369,8 +387,13 @@ export const GroupAnnouncements = ({
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if(!secretKey && isPrivate) return
|
||||
if (selectedGroup && !hasInitialized.current && !hide && isPrivate !== null) {
|
||||
if (!secretKey && isPrivate) return;
|
||||
if (
|
||||
selectedGroup &&
|
||||
!hasInitialized.current &&
|
||||
!hide &&
|
||||
isPrivate !== null
|
||||
) {
|
||||
getAnnouncements(selectedGroup, isPrivate);
|
||||
hasInitialized.current = true;
|
||||
}
|
||||
@ -384,9 +407,9 @@ export const GroupAnnouncements = ({
|
||||
const identifier = `grp-${selectedGroup}-anc-`;
|
||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const responseData = await response.json();
|
||||
@ -394,7 +417,10 @@ export const GroupAnnouncements = ({
|
||||
setAnnouncements((prev) => [...prev, ...responseData]);
|
||||
setIsLoading(false);
|
||||
for (const data of responseData) {
|
||||
getAnnouncementData({ name: data.name, identifier: data.identifier }, isPrivate);
|
||||
getAnnouncementData(
|
||||
{ name: data.name, identifier: data.identifier },
|
||||
isPrivate
|
||||
);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
@ -406,9 +432,9 @@ export const GroupAnnouncements = ({
|
||||
const identifier = `grp-${selectedGroup}-anc-`;
|
||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${0}&reverse=true&prefix=true`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const responseData = await response.json();
|
||||
@ -416,10 +442,13 @@ export const GroupAnnouncements = ({
|
||||
if (!latestMessage) {
|
||||
for (const data of responseData) {
|
||||
try {
|
||||
getAnnouncementData({
|
||||
name: data.name,
|
||||
identifier: data.identifier,
|
||||
}, isPrivate);
|
||||
getAnnouncementData(
|
||||
{
|
||||
name: data.name,
|
||||
identifier: data.identifier,
|
||||
},
|
||||
isPrivate
|
||||
);
|
||||
} catch (error) {}
|
||||
}
|
||||
setAnnouncements(responseData);
|
||||
@ -434,7 +463,10 @@ export const GroupAnnouncements = ({
|
||||
|
||||
for (const data of newArray) {
|
||||
try {
|
||||
getAnnouncementData({ name: data.name, identifier: data.identifier }, isPrivate);
|
||||
getAnnouncementData(
|
||||
{ name: data.name, identifier: data.identifier },
|
||||
isPrivate
|
||||
);
|
||||
} catch (error) {}
|
||||
}
|
||||
setAnnouncements((prev) => [...newArray, ...prev]);
|
||||
@ -486,13 +518,15 @@ export const GroupAnnouncements = ({
|
||||
<div
|
||||
style={{
|
||||
// reference to change height
|
||||
height: isMobile ? `calc(${rootHeight} - 127px` : "calc(100vh - 70px)",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
visibility: hide && "hidden",
|
||||
position: hide && "fixed",
|
||||
left: hide && "-1000px",
|
||||
height: isMobile
|
||||
? `calc(${rootHeight} - 127px`
|
||||
: 'calc(100vh - 70px)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
visibility: hide && 'hidden',
|
||||
position: hide && 'fixed',
|
||||
left: hide && '-1000px',
|
||||
}}
|
||||
>
|
||||
<AnnouncementDiscussion
|
||||
@ -509,63 +543,62 @@ export const GroupAnnouncements = ({
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
// reference to change height
|
||||
height: isMobile ? `calc(${rootHeight} - 127px` : "calc(100vh - 70px)",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
visibility: hide && "hidden",
|
||||
position: hide && "fixed",
|
||||
left: hide && "-1000px",
|
||||
// reference to change height
|
||||
height: isMobile ? `calc(${rootHeight} - 127px` : 'calc(100vh - 70px)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
visibility: hide && 'hidden',
|
||||
position: hide && 'fixed',
|
||||
left: hide && '-1000px',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{!isMobile && (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
padding: isMobile ? "8px" : "25px",
|
||||
fontSize: isMobile ? "16px" : "20px",
|
||||
gap: "20px",
|
||||
alignItems: "center",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
padding: isMobile ? '8px' : '25px',
|
||||
fontSize: isMobile ? '16px' : '20px',
|
||||
gap: '20px',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<CampaignIcon
|
||||
sx={{
|
||||
fontSize: isMobile ? "16px" : "30px",
|
||||
fontSize: isMobile ? '16px' : '30px',
|
||||
}}
|
||||
/>
|
||||
Group Announcements
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Spacer height={isMobile ? "0px" : "25px"} />
|
||||
<Spacer height={isMobile ? '0px' : '25px'} />
|
||||
</div>
|
||||
{!isLoading && combinedListTempAndReal?.length === 0 && (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
fontSize: '16px',
|
||||
}}
|
||||
>
|
||||
No announcements
|
||||
@ -589,28 +622,28 @@ export const GroupAnnouncements = ({
|
||||
style={{
|
||||
// position: 'fixed',
|
||||
// bottom: '0px',
|
||||
backgroundColor: "#232428",
|
||||
minHeight: isMobile ? "0px" : "150px",
|
||||
maxHeight: isMobile ? "auto" : "400px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflow: "hidden",
|
||||
width: "100%",
|
||||
boxSizing: "border-box",
|
||||
padding: isMobile ? "10px" : "20px",
|
||||
position: isFocusedParent ? "fixed" : "relative",
|
||||
bottom: isFocusedParent ? "0px" : "unset",
|
||||
top: isFocusedParent ? "0px" : "unset",
|
||||
zIndex: isFocusedParent ? 5 : "unset",
|
||||
backgroundColor: '#232428',
|
||||
minHeight: isMobile ? '0px' : '150px',
|
||||
maxHeight: isMobile ? 'auto' : '400px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
width: '100%',
|
||||
boxSizing: 'border-box',
|
||||
padding: isMobile ? '10px' : '20px',
|
||||
position: isFocusedParent ? 'fixed' : 'relative',
|
||||
bottom: isFocusedParent ? '0px' : 'unset',
|
||||
top: isFocusedParent ? '0px' : 'unset',
|
||||
zIndex: isFocusedParent ? 5 : 'unset',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: isMobile && 1,
|
||||
overflow: "auto",
|
||||
overflow: 'auto',
|
||||
// height: '100%',
|
||||
}}
|
||||
>
|
||||
@ -625,12 +658,12 @@ export const GroupAnnouncements = ({
|
||||
</div>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100&",
|
||||
gap: "10px",
|
||||
justifyContent: "center",
|
||||
display: 'flex',
|
||||
width: '100&',
|
||||
gap: '10px',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0,
|
||||
position: "relative",
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{isFocusedParent && (
|
||||
@ -639,19 +672,19 @@ export const GroupAnnouncements = ({
|
||||
if (isSending) return;
|
||||
setIsFocusedParent(false);
|
||||
clearEditorContent();
|
||||
setTimeout(() => {
|
||||
triggerRerender();
|
||||
}, 300);
|
||||
setTimeout(() => {
|
||||
triggerRerender();
|
||||
}, 300);
|
||||
// Unfocus the editor
|
||||
}}
|
||||
style={{
|
||||
marginTop: "auto",
|
||||
alignSelf: "center",
|
||||
cursor: isSending ? "default" : "pointer",
|
||||
background: "var(--danger)",
|
||||
marginTop: 'auto',
|
||||
alignSelf: 'center',
|
||||
cursor: isSending ? 'default' : 'pointer',
|
||||
background: 'var(--danger)',
|
||||
flexShrink: 0,
|
||||
padding: isMobile && "5px",
|
||||
fontSize: isMobile && "14px",
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
}}
|
||||
>
|
||||
{` Close`}
|
||||
@ -663,25 +696,25 @@ export const GroupAnnouncements = ({
|
||||
publishAnnouncement();
|
||||
}}
|
||||
style={{
|
||||
marginTop: "auto",
|
||||
alignSelf: "center",
|
||||
cursor: isSending ? "default" : "pointer",
|
||||
background: isSending && "rgba(0, 0, 0, 0.8)",
|
||||
marginTop: 'auto',
|
||||
alignSelf: 'center',
|
||||
cursor: isSending ? 'default' : 'pointer',
|
||||
background: isSending && 'rgba(0, 0, 0, 0.8)',
|
||||
flexShrink: 0,
|
||||
padding: isMobile && "5px",
|
||||
fontSize: isMobile && "14px",
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
}}
|
||||
>
|
||||
{isSending && (
|
||||
<CircularProgress
|
||||
size={18}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
marginTop: "-12px",
|
||||
marginLeft: "-12px",
|
||||
color: "white",
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
marginTop: '-12px',
|
||||
marginLeft: '-12px',
|
||||
color: 'white',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@ -701,7 +734,7 @@ export const GroupAnnouncements = ({
|
||||
<LoadingSnackbar
|
||||
open={isLoading}
|
||||
info={{
|
||||
message: "Loading announcements... please wait.",
|
||||
message: 'Loading announcements... please wait.',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -1,28 +1,31 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import syncedImg from '../assets/syncStatus/synced.png'
|
||||
import syncedMintingImg from '../assets/syncStatus/synced_minting.png'
|
||||
import syncingImg from '../assets/syncStatus/syncing.png'
|
||||
import { useEffect, useState } from 'react';
|
||||
import syncedImg from '../assets/syncStatus/synced.png';
|
||||
import syncedMintingImg from '../assets/syncStatus/synced_minting.png';
|
||||
import syncingImg from '../assets/syncStatus/syncing.png';
|
||||
import { getBaseApiReact } from '../App';
|
||||
import './CoreSyncStatus.css'
|
||||
export const CoreSyncStatus = ({imageSize, position}) => {
|
||||
import '../styles/CoreSyncStatus.css';
|
||||
import { useTheme } from '@mui/material';
|
||||
|
||||
export const CoreSyncStatus = () => {
|
||||
const [nodeInfos, setNodeInfos] = useState({});
|
||||
const [coreInfos, setCoreInfos] = useState({});
|
||||
const [isUsingGateway, setIsUsingGateway] = useState(false);
|
||||
const theme = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
const getNodeInfos = async () => {
|
||||
|
||||
|
||||
try {
|
||||
setIsUsingGateway(!!getBaseApiReact()?.includes('ext-node.qortal.link'))
|
||||
const url = `${getBaseApiReact()}/admin/status`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const data = await response.json();
|
||||
setIsUsingGateway(
|
||||
!!getBaseApiReact()?.includes('ext-node.qortal.link')
|
||||
);
|
||||
const url = `${getBaseApiReact()}/admin/status`;
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const data = await response.json();
|
||||
setNodeInfos(data);
|
||||
} catch (error) {
|
||||
console.error('Request failed', error);
|
||||
@ -30,14 +33,12 @@ export const CoreSyncStatus = ({imageSize, position}) => {
|
||||
};
|
||||
|
||||
const getCoreInfos = async () => {
|
||||
|
||||
|
||||
try {
|
||||
const url = `${getBaseApiReact()}/admin/info`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const data = await response.json();
|
||||
@ -59,55 +60,85 @@ export const CoreSyncStatus = ({imageSize, position}) => {
|
||||
}, []);
|
||||
|
||||
const renderSyncStatusIcon = () => {
|
||||
const { isSynchronizing = false, syncPercent = 0, isMintingPossible = false, height = 0, numberOfConnections = 0 } = nodeInfos;
|
||||
const buildVersion = coreInfos?.buildVersion ? coreInfos?.buildVersion.substring(0, 12) : '';
|
||||
const {
|
||||
isSynchronizing = false,
|
||||
syncPercent = 0,
|
||||
isMintingPossible = false,
|
||||
height = 0,
|
||||
numberOfConnections = 0,
|
||||
} = nodeInfos;
|
||||
const buildVersion = coreInfos?.buildVersion
|
||||
? coreInfos?.buildVersion.substring(0, 12)
|
||||
: '';
|
||||
|
||||
let imagePath = syncingImg;
|
||||
let message = `Synchronizing`
|
||||
let message = `Synchronizing`;
|
||||
if (isMintingPossible && !isUsingGateway) {
|
||||
imagePath = syncedMintingImg;
|
||||
message = `${isSynchronizing ? 'Synchronizing' : 'Synchronized'} ${'(Minting)'}`
|
||||
message = `${isSynchronizing ? 'Synchronizing' : 'Synchronized'} ${'(Minting)'}`;
|
||||
} else if (isSynchronizing === true && syncPercent === 99) {
|
||||
imagePath = syncingImg
|
||||
imagePath = syncingImg;
|
||||
} else if (isSynchronizing && !isMintingPossible && syncPercent === 100) {
|
||||
imagePath = syncingImg;
|
||||
message = `Synchronizing ${isUsingGateway ? '' :'(Not Minting)'}`
|
||||
message = `Synchronizing ${isUsingGateway ? '' : '(Not Minting)'}`;
|
||||
} else if (!isSynchronizing && !isMintingPossible && syncPercent === 100) {
|
||||
imagePath = syncedImg
|
||||
message = `Synchronized ${isUsingGateway ? '' :'(Not Minting)'}`
|
||||
imagePath = syncedImg;
|
||||
message = `Synchronized ${isUsingGateway ? '' : '(Not Minting)'}`;
|
||||
} else if (isSynchronizing && isMintingPossible && syncPercent === 100) {
|
||||
imagePath = syncingImg;
|
||||
message = `Synchronizing ${isUsingGateway ? '' :'(Minting)'}`
|
||||
message = `Synchronizing ${isUsingGateway ? '' : '(Minting)'}`;
|
||||
} else if (!isSynchronizing && isMintingPossible && syncPercent === 100) {
|
||||
imagePath = syncedMintingImg;
|
||||
message = `Synchronized ${isUsingGateway ? '' :'(Minting)'}`
|
||||
message = `Synchronized ${isUsingGateway ? '' : '(Minting)'}`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="tooltip" style={{ display: 'inline' }}>
|
||||
<span><img src={imagePath} style={{ height: 'auto', width: imageSize ? imageSize : '24px' }} alt="sync status" /></span>
|
||||
<div className="bottom" style={{
|
||||
right: position && 'unset',
|
||||
left: position && '0px'
|
||||
}}>
|
||||
<div
|
||||
className="tooltip"
|
||||
data-theme={theme.palette.mode}
|
||||
style={{ display: 'inline' }}
|
||||
>
|
||||
<span>
|
||||
<img
|
||||
src={imagePath}
|
||||
style={{ height: 'auto', width: '24px' }}
|
||||
alt="sync status"
|
||||
/>
|
||||
</span>
|
||||
<div
|
||||
className="bottom"
|
||||
style={{
|
||||
right: 'unset',
|
||||
left: '0px',
|
||||
}}
|
||||
>
|
||||
<h3>Core Information</h3>
|
||||
<h4 className="lineHeight">Core Version: <span style={{ color: '#03a9f4' }}>{buildVersion}</span></h4>
|
||||
<h4 className="lineHeight">
|
||||
Core Version:{' '}
|
||||
<span style={{ color: '#03a9f4' }}>{buildVersion}</span>
|
||||
</h4>
|
||||
<h4 className="lineHeight">{message}</h4>
|
||||
<h4 className="lineHeight">Block Height: <span style={{ color: '#03a9f4' }}>{height || ''}</span></h4>
|
||||
<h4 className="lineHeight">Connected Peers: <span style={{ color: '#03a9f4' }}>{numberOfConnections || ''}</span></h4>
|
||||
<h4 className="lineHeight">Using public node: <span style={{ color: '#03a9f4' }}>{isUsingGateway?.toString()}</span></h4>
|
||||
<h4 className="lineHeight">
|
||||
Block Height:{' '}
|
||||
<span style={{ color: '#03a9f4' }}>{height || ''}</span>
|
||||
</h4>
|
||||
<h4 className="lineHeight">
|
||||
Connected Peers:{' '}
|
||||
<span style={{ color: '#03a9f4' }}>
|
||||
{numberOfConnections || ''}
|
||||
</span>
|
||||
</h4>
|
||||
<h4 className="lineHeight">
|
||||
Using public node:{' '}
|
||||
<span style={{ color: '#03a9f4' }}>
|
||||
{isUsingGateway?.toString()}
|
||||
</span>
|
||||
</h4>
|
||||
<i></i>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div id="core-sync-status-id">
|
||||
{renderSyncStatusIcon()}
|
||||
</div>
|
||||
);
|
||||
return <div id="core-sync-status-id">{renderSyncStatusIcon()}</div>;
|
||||
};
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { ButtonBase, Typography, useTheme } from "@mui/material";
|
||||
import Box from "@mui/material/Box";
|
||||
import { HubsIcon } from "../../assets/Icons/HubsIcon";
|
||||
import { MessagingIcon } from "../../assets/Icons/MessagingIcon";
|
||||
import AppIcon from "../../assets/svgs/AppIcon.svg";
|
||||
import { ButtonBase, Typography, useTheme } from '@mui/material';
|
||||
import Box from '@mui/material/Box';
|
||||
import { HubsIcon } from '../../assets/Icons/HubsIcon';
|
||||
import { MessagingIcon } from '../../assets/Icons/MessagingIcon';
|
||||
import AppIcon from '../../assets/svgs/AppIcon.svg';
|
||||
|
||||
import { HomeIcon } from "../../assets/Icons/HomeIcon";
|
||||
import { Save } from "../Save/Save";
|
||||
import { useRecoilState } from "recoil";
|
||||
import { enabledDevModeAtom } from "../../atoms/global";
|
||||
import { HomeIcon } from '../../assets/Icons/HomeIcon';
|
||||
import { Save } from '../Save/Save';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { enabledDevModeAtom } from '../../atoms/global';
|
||||
|
||||
export const IconWrapper = ({
|
||||
children,
|
||||
@ -22,25 +22,25 @@ export const IconWrapper = ({
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
flexDirection: "column",
|
||||
height: customWidth ? customWidth : disableWidth ? "auto" : "89px",
|
||||
width: customWidth ? customWidth : disableWidth ? "auto" : "89px",
|
||||
borderRadius: "50%",
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: '5px',
|
||||
flexDirection: 'column',
|
||||
height: customWidth ? customWidth : disableWidth ? 'auto' : '89px',
|
||||
width: customWidth ? customWidth : disableWidth ? 'auto' : '89px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: selected
|
||||
? theme.palette.background.default
|
||||
: "transparent",
|
||||
: 'transparent',
|
||||
color: color ? color : theme.palette.text.primary,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Inter",
|
||||
fontSize: "12px",
|
||||
fontFamily: 'Inter',
|
||||
fontSize: '12px',
|
||||
fontWeight: 500,
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
@ -72,20 +72,20 @@ export const DesktopFooter = ({
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
position: "absolute",
|
||||
width: '100%',
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
height: "100px", // Footer height
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
height: '100px', // Footer height
|
||||
zIndex: 1,
|
||||
justifyContent: "center",
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
@ -94,15 +94,13 @@ export const DesktopFooter = ({
|
||||
}}
|
||||
>
|
||||
<IconWrapper label="Home" selected={isHome}>
|
||||
<HomeIcon
|
||||
height={30}
|
||||
/>
|
||||
<HomeIcon height={30} />
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopViewMode("apps");
|
||||
setDesktopViewMode('apps');
|
||||
setIsOpenSideViewDirects(false);
|
||||
setIsOpenSideViewGroups(false);
|
||||
}}
|
||||
@ -114,7 +112,7 @@ export const DesktopFooter = ({
|
||||
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopSideView("groups");
|
||||
setDesktopSideView('groups');
|
||||
}}
|
||||
>
|
||||
<IconWrapper label="Groups" selected={isGroups}>
|
||||
@ -122,10 +120,10 @@ export const DesktopFooter = ({
|
||||
height={30}
|
||||
color={
|
||||
hasUnreadGroups
|
||||
? "var(--danger)"
|
||||
? 'var(--danger)'
|
||||
: isGroups
|
||||
? "white"
|
||||
: "rgba(250, 250, 250, 0.5)"
|
||||
? 'white'
|
||||
: 'rgba(250, 250, 250, 0.5)'
|
||||
}
|
||||
/>
|
||||
</IconWrapper>
|
||||
@ -133,7 +131,7 @@ export const DesktopFooter = ({
|
||||
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopSideView("directs");
|
||||
setDesktopSideView('directs');
|
||||
}}
|
||||
>
|
||||
<IconWrapper label="Messaging" selected={isDirects}>
|
||||
@ -141,10 +139,10 @@ export const DesktopFooter = ({
|
||||
height={30}
|
||||
color={
|
||||
hasUnreadDirects
|
||||
? "var(--danger)"
|
||||
? 'var(--danger)'
|
||||
: isDirects
|
||||
? "white"
|
||||
: "rgba(250, 250, 250, 0.5)"
|
||||
? 'white'
|
||||
: 'rgba(250, 250, 250, 0.5)'
|
||||
}
|
||||
/>
|
||||
</IconWrapper>
|
||||
@ -154,7 +152,7 @@ export const DesktopFooter = ({
|
||||
{isEnabledDevMode && (
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopViewMode("dev");
|
||||
setDesktopViewMode('dev');
|
||||
setIsOpenSideViewDirects(false);
|
||||
setIsOpenSideViewGroups(false);
|
||||
}}
|
||||
|
@ -1,47 +1,55 @@
|
||||
import * as React from "react";
|
||||
import * as React from 'react';
|
||||
import {
|
||||
BottomNavigation,
|
||||
BottomNavigationAction,
|
||||
ButtonBase,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { Home, Groups, Message, ShowChart } from "@mui/icons-material";
|
||||
import Box from "@mui/material/Box";
|
||||
import BottomLogo from "../../assets/svgs/BottomLogo5.svg";
|
||||
import { CustomSvg } from "../../common/CustomSvg";
|
||||
import { WalletIcon } from "../../assets/Icons/WalletIcon";
|
||||
import { HubsIcon } from "../../assets/Icons/HubsIcon";
|
||||
import { TradingIcon } from "../../assets/Icons/TradingIcon";
|
||||
import { MessagingIcon } from "../../assets/Icons/MessagingIcon";
|
||||
import { HomeIcon } from "../../assets/Icons/HomeIcon";
|
||||
import { NotificationIcon2 } from "../../assets/Icons/NotificationIcon2";
|
||||
import { ChatIcon } from "../../assets/Icons/ChatIcon";
|
||||
import { ThreadsIcon } from "../../assets/Icons/ThreadsIcon";
|
||||
import { MembersIcon } from "../../assets/Icons/MembersIcon";
|
||||
import { AdminsIcon } from "../../assets/Icons/AdminsIcon";
|
||||
} from '@mui/material';
|
||||
import { Home, Groups, Message, ShowChart } from '@mui/icons-material';
|
||||
import Box from '@mui/material/Box';
|
||||
import BottomLogo from '../../assets/svgs/BottomLogo5.svg';
|
||||
import { CustomSvg } from '../../common/CustomSvg';
|
||||
import { HubsIcon } from '../../assets/Icons/HubsIcon';
|
||||
import { TradingIcon } from '../../assets/Icons/TradingIcon';
|
||||
import { MessagingIcon } from '../../assets/Icons/MessagingIcon';
|
||||
import { HomeIcon } from '../../assets/Icons/HomeIcon';
|
||||
import { NotificationIcon2 } from '../../assets/Icons/NotificationIcon2';
|
||||
import { ChatIcon } from '../../assets/Icons/ChatIcon';
|
||||
import { ThreadsIcon } from '../../assets/Icons/ThreadsIcon';
|
||||
import { MembersIcon } from '../../assets/Icons/MembersIcon';
|
||||
import { AdminsIcon } from '../../assets/Icons/AdminsIcon';
|
||||
import LockIcon from '@mui/icons-material/Lock';
|
||||
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
||||
|
||||
const IconWrapper = ({ children, label, color, selected, selectColor, customHeight }) => {
|
||||
const IconWrapper = ({
|
||||
children,
|
||||
label,
|
||||
color,
|
||||
selected,
|
||||
selectColor,
|
||||
customHeight,
|
||||
}) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
flexDirection: "column",
|
||||
height: customHeight ? customHeight : "65px",
|
||||
width: customHeight ? customHeight : "65px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: selected ? selectColor || "rgba(28, 29, 32, 1)" : "transparent",
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gap: '5px',
|
||||
flexDirection: 'column',
|
||||
height: customHeight ? customHeight : '65px',
|
||||
width: customHeight ? customHeight : '65px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: selected
|
||||
? selectColor || 'rgba(28, 29, 32, 1)'
|
||||
: 'transparent',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
<Typography
|
||||
sx={{
|
||||
fontFamily: "Inter",
|
||||
fontSize: "10px",
|
||||
fontFamily: 'Inter',
|
||||
fontSize: '10px',
|
||||
fontWeight: 500,
|
||||
color: color,
|
||||
}}
|
||||
@ -83,60 +91,67 @@ export const DesktopHeader = ({
|
||||
isChat,
|
||||
isForum,
|
||||
setGroupSection,
|
||||
isPrivate
|
||||
isPrivate,
|
||||
}) => {
|
||||
const [value, setValue] = React.useState(0);
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
height: "70px", // Footer height
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
height: '70px', // Footer height
|
||||
zIndex: 1,
|
||||
justifyContent: "space-between",
|
||||
padding: "10px",
|
||||
justifyContent: 'space-between',
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
gap: '10px'
|
||||
}}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
{isPrivate && (
|
||||
<LockIcon sx={{
|
||||
color: 'var(--green)'
|
||||
}} />
|
||||
<LockIcon
|
||||
sx={{
|
||||
color: 'var(--green)',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{isPrivate === false && (
|
||||
<NoEncryptionGmailerrorredIcon sx={{
|
||||
color: 'var(--danger)'
|
||||
}} />
|
||||
<NoEncryptionGmailerrorredIcon
|
||||
sx={{
|
||||
color: 'var(--danger)',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
fontSize: '16px',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{selectedGroup?.groupId === '0' ? 'General' :selectedGroup?.groupName}
|
||||
{selectedGroup?.groupId === '0'
|
||||
? 'General'
|
||||
: selectedGroup?.groupName}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
alignItems: "center",
|
||||
visibility: selectedGroup?.groupId === '0' ? 'hidden' : 'visibile'
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
alignItems: 'center',
|
||||
visibility: selectedGroup?.groupId === '0' ? 'hidden' : 'visibile',
|
||||
}}
|
||||
>
|
||||
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
goToAnnouncements()
|
||||
goToAnnouncements();
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color={isAnnouncement ? "black" :"rgba(250, 250, 250, 0.5)"}
|
||||
color={isAnnouncement ? 'black' : 'rgba(250, 250, 250, 0.5)'}
|
||||
label="ANN"
|
||||
selected={isAnnouncement}
|
||||
selectColor="#09b6e8"
|
||||
@ -147,10 +162,10 @@ export const DesktopHeader = ({
|
||||
width={20}
|
||||
color={
|
||||
isUnread
|
||||
? "var(--unread)"
|
||||
? 'var(--unread)'
|
||||
: isAnnouncement
|
||||
? "black"
|
||||
: "rgba(250, 250, 250, 0.5)"
|
||||
? 'black'
|
||||
: 'rgba(250, 250, 250, 0.5)'
|
||||
}
|
||||
/>
|
||||
</IconWrapper>
|
||||
@ -158,11 +173,11 @@ export const DesktopHeader = ({
|
||||
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
goToChat()
|
||||
goToChat();
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color={isChat ? "black" :"rgba(250, 250, 250, 0.5)"}
|
||||
color={isChat ? 'black' : 'rgba(250, 250, 250, 0.5)'}
|
||||
label="Chat"
|
||||
selected={isChat}
|
||||
selectColor="#09b6e8"
|
||||
@ -173,10 +188,10 @@ export const DesktopHeader = ({
|
||||
width={20}
|
||||
color={
|
||||
isUnreadChat
|
||||
? "var(--unread)"
|
||||
? 'var(--unread)'
|
||||
: isChat
|
||||
? "black"
|
||||
: "rgba(250, 250, 250, 0.5)"
|
||||
? 'black'
|
||||
: 'rgba(250, 250, 250, 0.5)'
|
||||
}
|
||||
/>
|
||||
</IconWrapper>
|
||||
@ -184,12 +199,11 @@ export const DesktopHeader = ({
|
||||
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setGroupSection("forum");
|
||||
|
||||
setGroupSection('forum');
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color={isForum ? 'black' : "rgba(250, 250, 250, 0.5)"}
|
||||
color={isForum ? 'black' : 'rgba(250, 250, 250, 0.5)'}
|
||||
label="Threads"
|
||||
selected={isForum}
|
||||
selectColor="#09b6e8"
|
||||
@ -198,18 +212,13 @@ export const DesktopHeader = ({
|
||||
<ThreadsIcon
|
||||
height={25}
|
||||
width={20}
|
||||
color={
|
||||
isForum
|
||||
? "black"
|
||||
: "rgba(250, 250, 250, 0.5)"
|
||||
}
|
||||
color={isForum ? 'black' : 'rgba(250, 250, 250, 0.5)'}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setOpenManageMembers(true)
|
||||
|
||||
setOpenManageMembers(true);
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
@ -221,22 +230,21 @@ export const DesktopHeader = ({
|
||||
<MembersIcon
|
||||
height={25}
|
||||
width={20}
|
||||
color={
|
||||
isForum
|
||||
? "white"
|
||||
: "rgba(250, 250, 250, 0.5)"
|
||||
}
|
||||
color={isForum ? 'white' : 'rgba(250, 250, 250, 0.5)'}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setGroupSection("adminSpace");
|
||||
|
||||
setGroupSection('adminSpace');
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color={groupSection === 'adminSpace' ? 'black' : "rgba(250, 250, 250, 0.5)"}
|
||||
color={
|
||||
groupSection === 'adminSpace'
|
||||
? 'black'
|
||||
: 'rgba(250, 250, 250, 0.5)'
|
||||
}
|
||||
label="Admins"
|
||||
selected={groupSection === 'adminSpace'}
|
||||
customHeight="55px"
|
||||
@ -247,8 +255,8 @@ export const DesktopHeader = ({
|
||||
width={20}
|
||||
color={
|
||||
groupSection === 'adminSpace'
|
||||
? "black"
|
||||
: "rgba(250, 250, 250, 0.5)"
|
||||
? 'black'
|
||||
: 'rgba(250, 250, 250, 0.5)'
|
||||
}
|
||||
/>
|
||||
</IconWrapper>
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { Box, ButtonBase, useTheme } from "@mui/material";
|
||||
import { HomeIcon } from "../assets/Icons/HomeIcon";
|
||||
import { MessagingIcon } from "../assets/Icons/MessagingIcon";
|
||||
import { Save } from "./Save/Save";
|
||||
import { IconWrapper } from "./Desktop/DesktopFooter";
|
||||
import { useRecoilState } from "recoil";
|
||||
import { enabledDevModeAtom } from "../atoms/global";
|
||||
import { AppsIcon } from "../assets/Icons/AppsIcon";
|
||||
import ThemeSelector from "./Theme/ThemeSelector";
|
||||
import { Box, ButtonBase, useTheme } from '@mui/material';
|
||||
import { HomeIcon } from '../assets/Icons/HomeIcon';
|
||||
import { MessagingIcon } from '../assets/Icons/MessagingIcon';
|
||||
import { Save } from './Save/Save';
|
||||
import { IconWrapper } from './Desktop/DesktopFooter';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { enabledDevModeAtom } from '../atoms/global';
|
||||
import { AppsIcon } from '../assets/Icons/AppsIcon';
|
||||
import ThemeSelector from './Theme/ThemeSelector';
|
||||
|
||||
export const DesktopSideBar = ({
|
||||
goToHome,
|
||||
@ -24,25 +24,25 @@ export const DesktopSideBar = ({
|
||||
}) => {
|
||||
const [isEnabledDevMode, setIsEnabledDevMode] =
|
||||
useRecoilState(enabledDevModeAtom);
|
||||
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "60px",
|
||||
flexDirection: "column",
|
||||
height: "100vh",
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
gap: "25px",
|
||||
width: '60px',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
gap: '25px',
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
sx={{
|
||||
width: "60px",
|
||||
height: "60px",
|
||||
paddingTop: "23px",
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
paddingTop: '23px',
|
||||
}}
|
||||
onClick={() => {
|
||||
goToHome();
|
||||
@ -51,39 +51,39 @@ export const DesktopSideBar = ({
|
||||
<HomeIcon
|
||||
height={34}
|
||||
color={
|
||||
desktopViewMode === "home" ? "white" : "rgba(250, 250, 250, 0.5)"
|
||||
desktopViewMode === 'home' ? 'white' : 'rgba(250, 250, 250, 0.5)'
|
||||
}
|
||||
/>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopViewMode("apps");
|
||||
setDesktopViewMode('apps');
|
||||
// setIsOpenSideViewDirects(false)
|
||||
// setIsOpenSideViewGroups(false)
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color={isApps ? "white" : "rgba(250, 250, 250, 0.5)"}
|
||||
color={isApps ? 'white' : 'rgba(250, 250, 250, 0.5)'}
|
||||
label="Apps"
|
||||
selected={isApps}
|
||||
disableWidth
|
||||
>
|
||||
<AppsIcon
|
||||
color={isApps ? "white" : "rgba(250, 250, 250, 0.5)"}
|
||||
color={isApps ? 'white' : 'rgba(250, 250, 250, 0.5)'}
|
||||
height={30}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopViewMode("chat");
|
||||
setDesktopViewMode('chat');
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color={
|
||||
hasUnreadDirects || hasUnreadGroups
|
||||
? "var(--unread)"
|
||||
: theme.palette.text.primary
|
||||
? 'var(--unread)'
|
||||
: theme.palette.text.primary
|
||||
}
|
||||
label="Chat"
|
||||
disableWidth
|
||||
@ -92,7 +92,7 @@ export const DesktopSideBar = ({
|
||||
height={30}
|
||||
color={
|
||||
hasUnreadDirects || hasUnreadGroups
|
||||
? "var(--unread)"
|
||||
? 'var(--unread)'
|
||||
: theme.palette.text.primary
|
||||
}
|
||||
/>
|
||||
@ -121,24 +121,22 @@ export const DesktopSideBar = ({
|
||||
{isEnabledDevMode && (
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopViewMode("dev");
|
||||
setDesktopViewMode('dev');
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color={
|
||||
desktopViewMode === "dev" ? "white" : "rgba(250, 250, 250, 0.5)"
|
||||
desktopViewMode === 'dev' ? 'white' : 'rgba(250, 250, 250, 0.5)'
|
||||
}
|
||||
label="Dev"
|
||||
disableWidth
|
||||
>
|
||||
<AppsIcon
|
||||
height={30}
|
||||
/>
|
||||
<AppsIcon height={30} />
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
)}
|
||||
|
||||
<ThemeSelector style={{ position: "fixed", bottom: "1%" }} />
|
||||
<ThemeSelector style={{ position: 'fixed', bottom: '1%' }} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
ButtonBase,
|
||||
@ -8,58 +7,72 @@ import {
|
||||
Popover,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import NotificationsIcon from "@mui/icons-material/Notifications";
|
||||
import AccountBalanceWalletIcon from "@mui/icons-material/AccountBalanceWallet";
|
||||
import { formatDate } from "../utils/time";
|
||||
import { useHandlePaymentNotification } from "../hooks/useHandlePaymentNotification";
|
||||
import { executeEvent } from "../utils/events";
|
||||
useTheme,
|
||||
} from '@mui/material';
|
||||
import NotificationsIcon from '@mui/icons-material/Notifications';
|
||||
import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet';
|
||||
import { formatDate } from '../utils/time';
|
||||
import { useHandlePaymentNotification } from '../hooks/useHandlePaymentNotification';
|
||||
import { executeEvent } from '../utils/events';
|
||||
|
||||
export const GeneralNotifications = ({ address }) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const {latestTx,
|
||||
|
||||
const {
|
||||
latestTx,
|
||||
getNameOrAddressOfSenderMiddle,
|
||||
hasNewPayment, setLastEnteredTimestampPayment, nameAddressOfSender} = useHandlePaymentNotification(address)
|
||||
|
||||
hasNewPayment,
|
||||
setLastEnteredTimestampPayment,
|
||||
nameAddressOfSender,
|
||||
} = useHandlePaymentNotification(address);
|
||||
|
||||
const handlePopupClick = (event) => {
|
||||
event.stopPropagation(); // Prevent parent onClick from firing
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<>
|
||||
<ButtonBase
|
||||
onClick={(e) => {
|
||||
handlePopupClick(e);
|
||||
|
||||
|
||||
}}
|
||||
style={{}}
|
||||
>
|
||||
<Tooltip
|
||||
title={<span style={{ color: "white", fontSize: "14px", fontWeight: 700 }}>PAYMENT NOTIFICATION</span>}
|
||||
placement="left"
|
||||
arrow
|
||||
sx={{ fontSize: "24" }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: "#ffffff",
|
||||
backgroundColor: "#444444",
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: "#444444",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<NotificationsIcon
|
||||
sx={{
|
||||
color: hasNewPayment ? "var(--unread)" : "rgba(255, 255, 255, 0.5)",
|
||||
<Tooltip
|
||||
title={
|
||||
<span style={{ color: 'white', fontSize: '14px', fontWeight: 700 }}>
|
||||
PAYMENT NOTIFICATION
|
||||
</span>
|
||||
}
|
||||
placement="left"
|
||||
arrow
|
||||
sx={{ fontSize: '24' }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: '#ffffff',
|
||||
backgroundColor: '#444444',
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: '#444444',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<NotificationsIcon
|
||||
sx={{
|
||||
color: hasNewPayment
|
||||
? 'var(--unread)'
|
||||
: theme.palette.mode === 'dark'
|
||||
? 'rgb(209, 209, 209)'
|
||||
: 'rgba(41, 41, 43, 1)',
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</ButtonBase>
|
||||
|
||||
@ -67,81 +80,91 @@ export const GeneralNotifications = ({ address }) => {
|
||||
open={!!anchorEl}
|
||||
anchorEl={anchorEl}
|
||||
onClose={() => {
|
||||
if(hasNewPayment){
|
||||
setLastEnteredTimestampPayment(Date.now())
|
||||
if (hasNewPayment) {
|
||||
setLastEnteredTimestampPayment(Date.now());
|
||||
}
|
||||
setAnchorEl(null)
|
||||
|
||||
setAnchorEl(null);
|
||||
}} // Close popover on click outside
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "300px",
|
||||
maxWidth: "100%",
|
||||
maxHeight: "60vh",
|
||||
overflow: "auto",
|
||||
padding: "5px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: hasNewPayment ? "flex-start" : "center",
|
||||
width: '300px',
|
||||
maxWidth: '100%',
|
||||
maxHeight: '60vh',
|
||||
overflow: 'auto',
|
||||
padding: '5px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: hasNewPayment ? 'flex-start' : 'center',
|
||||
}}
|
||||
>
|
||||
{!hasNewPayment && <Typography sx={{
|
||||
userSelect: 'none'
|
||||
}}>No new notifications</Typography>}
|
||||
{!hasNewPayment && (
|
||||
<Typography
|
||||
sx={{
|
||||
userSelect: 'none',
|
||||
}}
|
||||
>
|
||||
No new notifications
|
||||
</Typography>
|
||||
)}
|
||||
{hasNewPayment && (
|
||||
<MenuItem
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "5px",
|
||||
width: "100%",
|
||||
alignItems: "flex-start",
|
||||
textWrap: "auto",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '5px',
|
||||
width: '100%',
|
||||
alignItems: 'flex-start',
|
||||
textWrap: 'auto',
|
||||
}}
|
||||
onClick={() => {
|
||||
setAnchorEl(null)
|
||||
executeEvent('openWalletsApp', {})
|
||||
onClick={() => {
|
||||
setAnchorEl(null);
|
||||
executeEvent('openWalletsApp', {});
|
||||
}}
|
||||
>
|
||||
<Card sx={{
|
||||
padding: '10px',
|
||||
width: '100%',
|
||||
backgroundColor: "#1F2023",
|
||||
gap: '5px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
}}>
|
||||
<Box
|
||||
<Card
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
justifyContent: "space-between",
|
||||
padding: '10px',
|
||||
width: '100%',
|
||||
backgroundColor: '#1F2023',
|
||||
gap: '5px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<AccountBalanceWalletIcon
|
||||
<Box
|
||||
sx={{
|
||||
color: "white",
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '5px',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
/>{" "}
|
||||
{formatDate(latestTx?.timestamp)}
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
|
||||
<Typography>{latestTx?.amount}</Typography>
|
||||
</Box>
|
||||
<Typography sx={{
|
||||
fontSize: '0.8rem'
|
||||
}}>{nameAddressOfSender.current[latestTx?.creatorAddress] || getNameOrAddressOfSenderMiddle(latestTx?.creatorAddress)}</Typography>
|
||||
|
||||
>
|
||||
<AccountBalanceWalletIcon
|
||||
sx={{
|
||||
color: 'white',
|
||||
}}
|
||||
/>{' '}
|
||||
{formatDate(latestTx?.timestamp)}
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '5px',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<Typography>{latestTx?.amount}</Typography>
|
||||
</Box>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '0.8rem',
|
||||
}}
|
||||
>
|
||||
{nameAddressOfSender.current[latestTx?.creatorAddress] ||
|
||||
getNameOrAddressOfSenderMiddle(latestTx?.creatorAddress)}
|
||||
</Typography>
|
||||
</Card>
|
||||
</MenuItem>
|
||||
)}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useContext, useEffect, useMemo, useState } from "react";
|
||||
import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
|
||||
import React, { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
@ -9,12 +9,12 @@ import {
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { CustomButton, CustomButtonAccept } from "../../App-styles";
|
||||
import { getBaseApiReact, MyContext } from "../../App";
|
||||
import { getFee } from "../../background";
|
||||
import { CustomizedSnackbars } from "../Snackbar/Snackbar";
|
||||
import { FidgetSpinner } from "react-loader-spinner";
|
||||
} from '@mui/material';
|
||||
import { CustomButton, CustomButtonAccept } from '../../styles/App-styles';
|
||||
import { getBaseApiReact, MyContext } from '../../App';
|
||||
import { getFee } from '../../background';
|
||||
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||
import { FidgetSpinner } from 'react-loader-spinner';
|
||||
|
||||
export const JoinGroup = ({ memberGroups }) => {
|
||||
const { show, setTxList } = useContext(MyContext);
|
||||
@ -42,43 +42,45 @@ export const JoinGroup = ({ memberGroups }) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("globalActionJoinGroup", handleJoinGroup);
|
||||
subscribeToEvent('globalActionJoinGroup', handleJoinGroup);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("globalActionJoinGroup", handleJoinGroup);
|
||||
unsubscribeFromEvent('globalActionJoinGroup', handleJoinGroup);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const isInGroup = useMemo(()=> {
|
||||
return !!memberGroups.find((item)=> +item?.groupId === +groupInfo?.groupId)
|
||||
}, [memberGroups, groupInfo])
|
||||
const isInGroup = useMemo(() => {
|
||||
return !!memberGroups.find(
|
||||
(item) => +item?.groupId === +groupInfo?.groupId
|
||||
);
|
||||
}, [memberGroups, groupInfo]);
|
||||
const joinGroup = async (group, isOpen) => {
|
||||
try {
|
||||
const groupId = group.groupId;
|
||||
const fee = await getFee("JOIN_GROUP");
|
||||
const fee = await getFee('JOIN_GROUP');
|
||||
await show({
|
||||
message: "Would you like to perform an JOIN_GROUP transaction?",
|
||||
publishFee: fee.fee + " QORT",
|
||||
message: 'Would you like to perform an JOIN_GROUP transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
setIsLoadingJoinGroup(true);
|
||||
await new Promise((res, rej) => {
|
||||
window
|
||||
.sendMessage("joinGroup", {
|
||||
.sendMessage('joinGroup', {
|
||||
groupId,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
setInfoSnack({
|
||||
type: "success",
|
||||
type: 'success',
|
||||
message:
|
||||
"Successfully requested to join group. It may take a couple of minutes for the changes to propagate",
|
||||
'Successfully requested to join group. It may take a couple of minutes for the changes to propagate',
|
||||
});
|
||||
|
||||
if (isOpen) {
|
||||
setTxList((prev) => [
|
||||
{
|
||||
...response,
|
||||
type: "joined-group",
|
||||
type: 'joined-group',
|
||||
label: `Joined Group ${group?.groupName}: awaiting confirmation`,
|
||||
labelDone: `Joined Group ${group?.groupName}: success!`,
|
||||
done: false,
|
||||
@ -90,7 +92,7 @@ export const JoinGroup = ({ memberGroups }) => {
|
||||
setTxList((prev) => [
|
||||
{
|
||||
...response,
|
||||
type: "joined-group-request",
|
||||
type: 'joined-group-request',
|
||||
label: `Requested to join Group ${group?.groupName}: awaiting confirmation`,
|
||||
labelDone: `Requested to join Group ${group?.groupName}: success!`,
|
||||
done: false,
|
||||
@ -105,7 +107,7 @@ export const JoinGroup = ({ memberGroups }) => {
|
||||
return;
|
||||
} else {
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
type: 'error',
|
||||
message: response?.error,
|
||||
});
|
||||
setOpenSnack(true);
|
||||
@ -114,8 +116,8 @@ export const JoinGroup = ({ memberGroups }) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
message: error.message || "An error occurred",
|
||||
type: 'error',
|
||||
message: error.message || 'An error occurred',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
rej(error);
|
||||
@ -138,37 +140,37 @@ export const JoinGroup = ({ memberGroups }) => {
|
||||
{!groupInfo && (
|
||||
<Box
|
||||
sx={{
|
||||
width: "325px",
|
||||
height: "150px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: '325px',
|
||||
height: '150px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
{' '}
|
||||
<CircularProgress
|
||||
size={25}
|
||||
sx={{
|
||||
color: "white",
|
||||
color: 'white',
|
||||
}}
|
||||
/>{" "}
|
||||
/>{' '}
|
||||
</Box>
|
||||
)}
|
||||
<Box
|
||||
sx={{
|
||||
width: "325px",
|
||||
height: "auto",
|
||||
maxHeight: "400px",
|
||||
display: !groupInfo ? "none" : "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
padding: "10px",
|
||||
width: '325px',
|
||||
height: 'auto',
|
||||
maxHeight: '400px',
|
||||
display: !groupInfo ? 'none' : 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '10px',
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "15px",
|
||||
fontSize: '15px',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
@ -176,7 +178,7 @@ export const JoinGroup = ({ memberGroups }) => {
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "15px",
|
||||
fontSize: '15px',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
@ -185,7 +187,7 @@ export const JoinGroup = ({ memberGroups }) => {
|
||||
{groupInfo?.description && (
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "15px",
|
||||
fontSize: '15px',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
@ -193,19 +195,19 @@ export const JoinGroup = ({ memberGroups }) => {
|
||||
</Typography>
|
||||
)}
|
||||
{isInGroup && (
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
*You are already in this group!
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
*You are already in this group!
|
||||
</Typography>
|
||||
)}
|
||||
{!isInGroup && groupInfo?.isOpen === false && (
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
fontSize: '14px',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
@ -216,32 +218,34 @@ export const JoinGroup = ({ memberGroups }) => {
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<ButtonBase onClick={() => {
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
joinGroup(groupInfo, groupInfo?.isOpen);
|
||||
|
||||
setIsOpen(false);
|
||||
}} disabled={isInGroup}>
|
||||
<CustomButtonAccept
|
||||
color="black"
|
||||
bgColor="var(--green)"
|
||||
sx={{
|
||||
minWidth: "102px",
|
||||
height: "45px",
|
||||
fontSize: '16px',
|
||||
opacity: isInGroup ? 0.1 : 1
|
||||
}}
|
||||
|
||||
disabled={isInGroup}
|
||||
>
|
||||
Join
|
||||
</CustomButtonAccept>
|
||||
<CustomButtonAccept
|
||||
color="black"
|
||||
bgColor="var(--green)"
|
||||
sx={{
|
||||
minWidth: '102px',
|
||||
height: '45px',
|
||||
fontSize: '16px',
|
||||
opacity: isInGroup ? 0.1 : 1,
|
||||
}}
|
||||
>
|
||||
Join
|
||||
</CustomButtonAccept>
|
||||
</ButtonBase>
|
||||
|
||||
|
||||
<CustomButtonAccept
|
||||
color="black"
|
||||
bgColor="var(--danger)"
|
||||
sx={{
|
||||
minWidth: "102px",
|
||||
height: "45px",
|
||||
minWidth: '102px',
|
||||
height: '45px',
|
||||
}}
|
||||
onClick={() => setIsOpen(false)}
|
||||
>
|
||||
@ -259,14 +263,14 @@ export const JoinGroup = ({ memberGroups }) => {
|
||||
{isLoadingJoinGroup && (
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<FidgetSpinner
|
||||
|
@ -10,15 +10,23 @@ import {
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { MyContext } from "../../App";
|
||||
import { getBaseApiReact, MyContext } from "../../App";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
|
||||
export const BlockedUsersModal = ({ close }) => {
|
||||
import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
|
||||
import { validateAddress } from "../../utils/validateAddress";
|
||||
import { getNameInfo, requestQueueMemberNames } from "./Group";
|
||||
import { useModal } from "../../common/useModal";
|
||||
import { useRecoilState } from "recoil";
|
||||
import { isOpenBlockedModalAtom } from "../../atoms/global";
|
||||
import InfoIcon from '@mui/icons-material/Info';
|
||||
export const BlockedUsersModal = () => {
|
||||
const [isOpenBlockedModal, setIsOpenBlockedModal] = useRecoilState(isOpenBlockedModalAtom)
|
||||
const [hasChanged, setHasChanged] = useState(false);
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
const { getAllBlockedUsers, removeBlockFromList, addToBlockList } = useContext(MyContext);
|
||||
const [addressesWithNames, setAddressesWithNames] = useState({})
|
||||
const { isShow, onCancel, onOk, show, message } = useModal();
|
||||
const { getAllBlockedUsers, removeBlockFromList, addToBlockList, setOpenSnackGlobal, setInfoSnackCustom } =
|
||||
useContext(MyContext);
|
||||
const [blockedUsers, setBlockedUsers] = useState({
|
||||
addresses: {},
|
||||
names: {},
|
||||
@ -28,60 +36,162 @@ export const BlockedUsersModal = ({ close }) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if(!isOpenBlockedModal) return
|
||||
fetchBlockedUsers();
|
||||
}, []);
|
||||
}, [isOpenBlockedModal]);
|
||||
|
||||
const getNames = async () => {
|
||||
// const validApi = await findUsableApi();
|
||||
const addresses = Object.keys(blockedUsers?.addresses)
|
||||
const addressNames = {}
|
||||
|
||||
|
||||
const getMemNames = addresses.map(async (address) => {
|
||||
const name = await requestQueueMemberNames.enqueue(() => {
|
||||
return getNameInfo(address);
|
||||
});
|
||||
if (name) {
|
||||
addressNames[address] = name
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
await Promise.all(getMemNames);
|
||||
|
||||
setAddressesWithNames(addressNames)
|
||||
};
|
||||
|
||||
const blockUser = async (e, user?: string) => {
|
||||
try {
|
||||
const valUser = user || value
|
||||
if (!valUser) return;
|
||||
const isAddress = validateAddress(valUser);
|
||||
let userName = null;
|
||||
let userAddress = null;
|
||||
if (isAddress) {
|
||||
userAddress = valUser;
|
||||
const name = await getNameInfo(valUser);
|
||||
if (name) {
|
||||
userName = name;
|
||||
}
|
||||
}
|
||||
if (!isAddress) {
|
||||
const response = await fetch(`${getBaseApiReact()}/names/${valUser}`);
|
||||
const data = await response.json();
|
||||
if (!data?.owner) throw new Error("Name does not exist");
|
||||
if (data?.owner) {
|
||||
userAddress = data.owner;
|
||||
userName = valUser;
|
||||
}
|
||||
}
|
||||
if(!userName){
|
||||
await addToBlockList(userAddress, null);
|
||||
fetchBlockedUsers();
|
||||
setHasChanged(true);
|
||||
executeEvent('updateChatMessagesWithBlocks', true)
|
||||
setValue('')
|
||||
return
|
||||
}
|
||||
const responseModal = await show({
|
||||
userName,
|
||||
userAddress,
|
||||
});
|
||||
if (responseModal === "both") {
|
||||
await addToBlockList(userAddress, userName);
|
||||
} else if (responseModal === "address") {
|
||||
await addToBlockList(userAddress, null);
|
||||
} else if (responseModal === "name") {
|
||||
await addToBlockList(null, userName);
|
||||
}
|
||||
fetchBlockedUsers();
|
||||
setHasChanged(true);
|
||||
setValue('')
|
||||
if(user){
|
||||
setIsOpenBlockedModal(false)
|
||||
}
|
||||
if(responseModal === 'both' || responseModal === 'address'){
|
||||
executeEvent('updateChatMessagesWithBlocks', true)
|
||||
}
|
||||
} catch (error) {
|
||||
setOpenSnackGlobal(true);
|
||||
|
||||
setInfoSnackCustom({
|
||||
type: "error",
|
||||
message: error?.message || "Unable to block user",
|
||||
});
|
||||
}
|
||||
};
|
||||
const blockUserFromOutsideModalFunc = (e) => {
|
||||
const user = e.detail?.user;
|
||||
setIsOpenBlockedModal(true)
|
||||
blockUser(null, user)
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("blockUserFromOutside", blockUserFromOutsideModalFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("blockUserFromOutside", blockUserFromOutsideModalFunc);
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<Dialog
|
||||
open={true}
|
||||
open={isOpenBlockedModal}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle>Blocked Users</DialogTitle>
|
||||
<DialogContent sx={{
|
||||
padding: '20px'
|
||||
}}>
|
||||
<Box
|
||||
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
<DialogTitle>Blocked Users</DialogTitle>
|
||||
<DialogContent
|
||||
sx={{
|
||||
padding: "20px",
|
||||
}}
|
||||
>
|
||||
<TextField
|
||||
placeholder="Name"
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
setValue(e.target.value);
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
}}
|
||||
/>
|
||||
<Button variant="contained" onClick={async ()=> {
|
||||
try {
|
||||
if(!value) return
|
||||
await addToBlockList(undefined, value)
|
||||
fetchBlockedUsers()
|
||||
setHasChanged(true)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}}>Block</Button>
|
||||
</Box>
|
||||
|
||||
>
|
||||
<TextField
|
||||
placeholder="Name or address"
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
setValue(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
sx={{
|
||||
flexShrink: 0,
|
||||
}}
|
||||
variant="contained"
|
||||
onClick={blockUser}
|
||||
>
|
||||
Block
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{Object.entries(blockedUsers?.addresses).length > 0 && (
|
||||
<>
|
||||
<Spacer height="20px" />
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Blocked Users for Chat ( addresses )
|
||||
Blocked addresses- blocks processing of txs
|
||||
</DialogContentText>
|
||||
<Spacer height="10px" />
|
||||
<Button variant="contained" size="small" onClick={getNames}>Fetch names</Button>
|
||||
<Spacer height="10px" />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '10px'
|
||||
}}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
{Object.entries(blockedUsers?.addresses || {})?.map(
|
||||
([key, value]) => {
|
||||
return (
|
||||
@ -90,18 +200,22 @@ export const BlockedUsersModal = ({ close }) => {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
width: '100%',
|
||||
justifyContent: 'space-between'
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Typography>{key}</Typography>
|
||||
<Typography>{addressesWithNames[key] || key}</Typography>
|
||||
<Button
|
||||
sx={{
|
||||
flexShrink: 0,
|
||||
}}
|
||||
size="small"
|
||||
variant="contained"
|
||||
onClick={async () => {
|
||||
try {
|
||||
await removeBlockFromList(key, undefined);
|
||||
setHasChanged(true);
|
||||
setValue('')
|
||||
setValue("");
|
||||
fetchBlockedUsers();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@ -119,17 +233,19 @@ export const BlockedUsersModal = ({ close }) => {
|
||||
<>
|
||||
<Spacer height="20px" />
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Blocked Users for QDN and Chat (names)
|
||||
Blocked names for QDN
|
||||
</DialogContentText>
|
||||
<Spacer height="10px" />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '10px'
|
||||
}}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
{Object.entries(blockedUsers?.names || {})?.map(([key, value]) => {
|
||||
return (
|
||||
<Box
|
||||
@ -137,12 +253,16 @@ export const BlockedUsersModal = ({ close }) => {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
width: '100%',
|
||||
justifyContent: 'space-between'
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Typography>{key}</Typography>
|
||||
<Button
|
||||
size="small"
|
||||
sx={{
|
||||
flexShrink: 0,
|
||||
}}
|
||||
variant="contained"
|
||||
onClick={async () => {
|
||||
try {
|
||||
@ -175,16 +295,67 @@ export const BlockedUsersModal = ({ close }) => {
|
||||
},
|
||||
}}
|
||||
variant="contained"
|
||||
onClick={()=> {
|
||||
if(hasChanged){
|
||||
executeEvent('updateChatMessagesWithBlocks', true)
|
||||
onClick={() => {
|
||||
if (hasChanged) {
|
||||
executeEvent("updateChatMessagesWithBlocks", true);
|
||||
}
|
||||
close()
|
||||
setIsOpenBlockedModal(false);
|
||||
}}
|
||||
>
|
||||
close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
|
||||
<Dialog
|
||||
open={isShow}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{"Decide what to block"}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Blocking {message?.userName || message?.userAddress}
|
||||
</DialogContentText>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '10px',
|
||||
marginTop: '20px'
|
||||
}}>
|
||||
<InfoIcon sx={{
|
||||
color: 'fff'
|
||||
}}/> <Typography>Choose "block txs" or "all" to block chat messages </Typography>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
onOk("address");
|
||||
}}
|
||||
>
|
||||
Block txs
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
onOk("name");
|
||||
}}
|
||||
>
|
||||
Block QDN data
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
onOk("both");
|
||||
}}
|
||||
>
|
||||
Block All
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
@ -1,329 +0,0 @@
|
||||
import React, {
|
||||
FC,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState
|
||||
} from 'react'
|
||||
|
||||
import {
|
||||
Box,
|
||||
|
||||
Skeleton,
|
||||
|
||||
} from '@mui/material'
|
||||
import { ShowMessage } from './ShowMessageWithoutModal'
|
||||
// import {
|
||||
// setIsLoadingCustom,
|
||||
// } from '../../state/features/globalSlice'
|
||||
import { ComposeP, GroupContainer, GroupNameP, MailIconImg, ShowMessageReturnButton, SingleThreadParent, ThreadContainer, ThreadContainerFullWidth } from './Mail-styles'
|
||||
import { Spacer } from '../../../common/Spacer'
|
||||
import { threadIdentifier } from './GroupMail'
|
||||
import LazyLoad from '../../../common/LazyLoad'
|
||||
import ReturnSVG from '../../../assets/svgs/Return.svg'
|
||||
import { NewThread } from './NewThread'
|
||||
import { decryptPublishes } from '../../Chat/GroupAnnouncements'
|
||||
import { getBaseApi } from '../../../background'
|
||||
import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App'
|
||||
interface ThreadProps {
|
||||
currentThread: any
|
||||
groupInfo: any
|
||||
closeThread: () => void
|
||||
members: any
|
||||
}
|
||||
|
||||
const getEncryptedResource = async ({name, identifier, secretKey})=> {
|
||||
|
||||
const res = await fetch(
|
||||
`${getBaseApiReact()}/arbitrary/DOCUMENT/${name}/${identifier}?encoding=base64`
|
||||
);
|
||||
const data = await res.text();
|
||||
const response = await decryptPublishes([{ data }], secretKey);
|
||||
const messageData = response[0];
|
||||
return messageData.decryptedData
|
||||
|
||||
}
|
||||
|
||||
export const Thread = ({
|
||||
currentThread,
|
||||
groupInfo,
|
||||
closeThread,
|
||||
members,
|
||||
userInfo,
|
||||
secretKey,
|
||||
getSecretKey
|
||||
}: ThreadProps) => {
|
||||
const [messages, setMessages] = useState<any[]>([])
|
||||
const [hashMapMailMessages, setHashMapMailMessages] = useState({})
|
||||
const secretKeyRef = useRef(null)
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
secretKeyRef.current = secretKey;
|
||||
}, [secretKey]);
|
||||
const getIndividualMsg = async (message: any) => {
|
||||
try {
|
||||
const responseDataMessage = await getEncryptedResource({identifier: message.identifier, name: message.name, secretKey})
|
||||
|
||||
|
||||
const fullObject = {
|
||||
...message,
|
||||
...(responseDataMessage || {}),
|
||||
id: message.identifier
|
||||
}
|
||||
setHashMapMailMessages((prev)=> {
|
||||
return {
|
||||
...prev,
|
||||
[message.identifier]: fullObject
|
||||
}
|
||||
})
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
const getMailMessages = React.useCallback(
|
||||
async (groupInfo: any, reset?: boolean, hideAlert?: boolean) => {
|
||||
try {
|
||||
if(!hideAlert){
|
||||
// dispatch(setIsLoadingCustom('Loading messages'))
|
||||
|
||||
}
|
||||
let threadId = groupInfo.threadId
|
||||
|
||||
const offset = messages.length
|
||||
const identifier = `thmsg-${threadId}`
|
||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
const responseData = await response.json()
|
||||
let fullArrayMsg = reset ? [] : [...messages]
|
||||
let newMessages: any[] = []
|
||||
for (const message of responseData) {
|
||||
const index = fullArrayMsg.findIndex(
|
||||
(p) => p.identifier === message.identifier
|
||||
)
|
||||
if (index !== -1) {
|
||||
fullArrayMsg[index] = message
|
||||
} else {
|
||||
fullArrayMsg.push(message)
|
||||
getIndividualMsg(message)
|
||||
}
|
||||
}
|
||||
setMessages(fullArrayMsg)
|
||||
} catch (error) {
|
||||
} finally {
|
||||
if(!hideAlert){
|
||||
// dispatch(setIsLoadingCustom(null))
|
||||
}
|
||||
}
|
||||
},
|
||||
[messages, secretKey]
|
||||
)
|
||||
const getMessages = React.useCallback(async () => {
|
||||
if (!currentThread || !secretKey) return
|
||||
await getMailMessages(currentThread, true)
|
||||
}, [getMailMessages, currentThread, secretKey])
|
||||
const firstMount = useRef(false)
|
||||
|
||||
const saveTimestamp = useCallback((currentThread: any, username?: string)=> {
|
||||
if(!currentThread?.threadData?.groupId || !currentThread?.threadId || !username) return
|
||||
const threadIdForLocalStorage = `qmail_threads_${currentThread?.threadData?.groupId}_${currentThread?.threadId}`
|
||||
const threads = JSON.parse(
|
||||
localStorage.getItem(`qmail_threads_viewedtimestamp_${username}`) || "{}"
|
||||
);
|
||||
// Convert to an array of objects with identifier and all fields
|
||||
let dataArray = Object.entries(threads).map(([identifier, value]) => ({
|
||||
identifier,
|
||||
...(value as any),
|
||||
}));
|
||||
|
||||
// Sort the array based on timestamp in descending order
|
||||
dataArray.sort((a, b) => b.timestamp - a.timestamp);
|
||||
|
||||
// Slice the array to keep only the first 500 elements
|
||||
let latest500 = dataArray.slice(0, 500);
|
||||
|
||||
// Convert back to the original object format
|
||||
let latest500Data: any = {};
|
||||
latest500.forEach(item => {
|
||||
const { identifier, ...rest } = item;
|
||||
latest500Data[identifier] = rest;
|
||||
});
|
||||
latest500Data[threadIdForLocalStorage] = {
|
||||
timestamp: Date.now(),
|
||||
}
|
||||
localStorage.setItem(
|
||||
`qmail_threads_viewedtimestamp_${username}`,
|
||||
JSON.stringify(latest500Data)
|
||||
);
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
if (currentThread && secretKey) {
|
||||
getMessages()
|
||||
firstMount.current = true
|
||||
// saveTimestamp(currentThread, user.name)
|
||||
}
|
||||
}, [ currentThread, secretKey])
|
||||
const messageCallback = useCallback((msg: any) => {
|
||||
// dispatch(addToHashMapMail(msg))
|
||||
setMessages((prev) => [msg, ...prev])
|
||||
}, [])
|
||||
|
||||
const interval = useRef<any>(null)
|
||||
|
||||
const checkNewMessages = React.useCallback(
|
||||
async (groupInfo: any) => {
|
||||
try {
|
||||
let threadId = groupInfo.threadId
|
||||
|
||||
const identifier = `thmsg-${threadId}`
|
||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&offset=${0}&reverse=true&prefix=true`
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
const responseData = await response.json()
|
||||
const latestMessage = messages[0]
|
||||
if (!latestMessage) return
|
||||
const findMessage = responseData?.findIndex(
|
||||
(item: any) => item?.identifier === latestMessage?.identifier
|
||||
)
|
||||
let sliceLength = responseData.length
|
||||
if (findMessage !== -1) {
|
||||
sliceLength = findMessage
|
||||
}
|
||||
const newArray = responseData.slice(0, findMessage).reverse()
|
||||
let fullArrayMsg = [...messages]
|
||||
for (const message of newArray) {
|
||||
try {
|
||||
|
||||
const responseDataMessage = await getEncryptedResource({identifier: message.identifier, name: message.name, secretKey: secretKeyRef.current})
|
||||
|
||||
const fullObject = {
|
||||
...message,
|
||||
...(responseDataMessage || {}),
|
||||
id: message.identifier
|
||||
}
|
||||
setHashMapMailMessages((prev)=> {
|
||||
return {
|
||||
...prev,
|
||||
[message.identifier]: fullObject
|
||||
}
|
||||
})
|
||||
const index = messages.findIndex(
|
||||
(p) => p.identifier === fullObject.identifier
|
||||
)
|
||||
if (index !== -1) {
|
||||
fullArrayMsg[index] = fullObject
|
||||
} else {
|
||||
fullArrayMsg.unshift(fullObject)
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
setMessages(fullArrayMsg)
|
||||
} catch (error) {
|
||||
} finally {
|
||||
}
|
||||
},
|
||||
[messages]
|
||||
)
|
||||
|
||||
const checkNewMessagesFunc = useCallback(() => {
|
||||
let isCalling = false
|
||||
interval.current = setInterval(async () => {
|
||||
if (isCalling) return
|
||||
isCalling = true
|
||||
const res = await checkNewMessages(currentThread)
|
||||
isCalling = false
|
||||
}, 8000)
|
||||
}, [checkNewMessages, currentThread])
|
||||
|
||||
useEffect(() => {
|
||||
checkNewMessagesFunc()
|
||||
return () => {
|
||||
if (interval?.current) {
|
||||
clearInterval(interval.current)
|
||||
}
|
||||
}
|
||||
}, [checkNewMessagesFunc])
|
||||
|
||||
|
||||
|
||||
if (!currentThread) return null
|
||||
return (
|
||||
<GroupContainer
|
||||
sx={{
|
||||
position: "relative",
|
||||
overflow: 'auto',
|
||||
width: '100%'
|
||||
}}
|
||||
>
|
||||
|
||||
<NewThread
|
||||
groupInfo={groupInfo}
|
||||
isMessage={true}
|
||||
currentThread={currentThread}
|
||||
messageCallback={messageCallback}
|
||||
members={members}
|
||||
userInfo={userInfo}
|
||||
getSecretKey={getSecretKey}
|
||||
/>
|
||||
<ThreadContainerFullWidth>
|
||||
<ThreadContainer>
|
||||
<Spacer height="30px" />
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between'
|
||||
}}>
|
||||
<GroupNameP>{currentThread?.threadData?.title}</GroupNameP>
|
||||
|
||||
<ShowMessageReturnButton onClick={() => {
|
||||
setMessages([])
|
||||
closeThread()
|
||||
}}>
|
||||
<MailIconImg src={ReturnSVG} />
|
||||
<ComposeP>Return to Threads</ComposeP>
|
||||
</ShowMessageReturnButton>
|
||||
</Box>
|
||||
<Spacer height="60px" />
|
||||
{messages.map((message) => {
|
||||
let fullMessage = message
|
||||
|
||||
if (hashMapMailMessages[message?.identifier]) {
|
||||
fullMessage = hashMapMailMessages[message.identifier]
|
||||
return <ShowMessage key={message?.identifier} message={fullMessage} />
|
||||
}
|
||||
|
||||
return (
|
||||
<SingleThreadParent>
|
||||
|
||||
<Skeleton
|
||||
variant="rectangular"
|
||||
style={{
|
||||
width: '100%',
|
||||
height: 60,
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
/>
|
||||
|
||||
</SingleThreadParent>
|
||||
)
|
||||
})}
|
||||
</ThreadContainer>
|
||||
</ThreadContainerFullWidth>
|
||||
{messages.length >= 20 && (
|
||||
<LazyLoad onLoadMore={()=> getMailMessages(currentThread, false, true)}></LazyLoad>
|
||||
|
||||
)}
|
||||
|
||||
</GroupContainer>
|
||||
)
|
||||
}
|
@ -5,7 +5,7 @@ import React, {
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
} from 'react';
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
@ -14,8 +14,8 @@ import {
|
||||
IconButton,
|
||||
Skeleton,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { ShowMessage } from "./ShowMessageWithoutModal";
|
||||
} from '@mui/material';
|
||||
import { ShowMessage } from './ShowMessageWithoutModal';
|
||||
import {
|
||||
ComposeP,
|
||||
GroupContainer,
|
||||
@ -28,34 +28,33 @@ import {
|
||||
ThreadInfoColumn,
|
||||
ThreadInfoColumnNameP,
|
||||
ThreadInfoColumnTime,
|
||||
} from "./Mail-styles";
|
||||
import { Spacer } from "../../../common/Spacer";
|
||||
import { threadIdentifier } from "./GroupMail";
|
||||
import LazyLoad from "../../../common/LazyLoad";
|
||||
import ReturnSVG from "../../../assets/svgs/Return.svg";
|
||||
import { NewThread } from "./NewThread";
|
||||
} from './Mail-styles';
|
||||
import { Spacer } from '../../../common/Spacer';
|
||||
import { threadIdentifier } from './GroupMail';
|
||||
import ReturnSVG from '../../../assets/svgs/Return.svg';
|
||||
import { NewThread } from './NewThread';
|
||||
import {
|
||||
decryptPublishes,
|
||||
getTempPublish,
|
||||
handleUnencryptedPublishes,
|
||||
} from "../../Chat/GroupAnnouncements";
|
||||
import { LoadingSnackbar } from "../../Snackbar/LoadingSnackbar";
|
||||
import { subscribeToEvent, unsubscribeFromEvent } from "../../../utils/events";
|
||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||
} from '../../Chat/GroupAnnouncements';
|
||||
import { LoadingSnackbar } from '../../Snackbar/LoadingSnackbar';
|
||||
import { subscribeToEvent, unsubscribeFromEvent } from '../../../utils/events';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
import {
|
||||
getArbitraryEndpointReact,
|
||||
getBaseApiReact,
|
||||
isMobile,
|
||||
} from "../../../App";
|
||||
} from '../../../App';
|
||||
import {
|
||||
ArrowDownward as ArrowDownwardIcon,
|
||||
ArrowUpward as ArrowUpwardIcon,
|
||||
} from "@mui/icons-material";
|
||||
import { addDataPublishesFunc, getDataPublishesFunc } from "../Group";
|
||||
import { RequestQueueWithPromise } from "../../../utils/queue/queue";
|
||||
import { CustomLoader } from "../../../common/CustomLoader";
|
||||
import { WrapperUserAction } from "../../WrapperUserAction";
|
||||
import { formatTimestampForum } from "../../../utils/time";
|
||||
} from '@mui/icons-material';
|
||||
import { addDataPublishesFunc, getDataPublishesFunc } from '../Group';
|
||||
import { RequestQueueWithPromise } from '../../../utils/queue/queue';
|
||||
import { CustomLoader } from '../../../common/CustomLoader';
|
||||
import { WrapperUserAction } from '../../WrapperUserAction';
|
||||
import { formatTimestampForum } from '../../../utils/time';
|
||||
const requestQueueSaveToLocal = new RequestQueueWithPromise(1);
|
||||
const requestQueueDownloadPost = new RequestQueueWithPromise(3);
|
||||
interface ThreadProps {
|
||||
@ -65,14 +64,10 @@ interface ThreadProps {
|
||||
members: any;
|
||||
}
|
||||
|
||||
const getEncryptedResource = async ({
|
||||
name,
|
||||
identifier,
|
||||
secretKey,
|
||||
resource,
|
||||
groupId,
|
||||
dataPublishes,
|
||||
}, isPrivate) => {
|
||||
const getEncryptedResource = async (
|
||||
{ name, identifier, secretKey, resource, groupId, dataPublishes },
|
||||
isPrivate
|
||||
) => {
|
||||
let data = dataPublishes[`${name}-${identifier}`];
|
||||
if (
|
||||
!data ||
|
||||
@ -92,15 +87,18 @@ const getEncryptedResource = async ({
|
||||
};
|
||||
}
|
||||
data = await res.text();
|
||||
|
||||
if (data?.error || typeof data !== "string") return;
|
||||
|
||||
if (data?.error || typeof data !== 'string') return;
|
||||
await requestQueueSaveToLocal.enqueue(() => {
|
||||
return addDataPublishesFunc({ ...resource, data }, groupId, "thmsg");
|
||||
return addDataPublishesFunc({ ...resource, data }, groupId, 'thmsg');
|
||||
});
|
||||
} else {
|
||||
data = data.data;
|
||||
}
|
||||
const response = isPrivate === false ? handleUnencryptedPublishes([data]) : await decryptPublishes([{ data }], secretKey);
|
||||
const response =
|
||||
isPrivate === false
|
||||
? handleUnencryptedPublishes([data])
|
||||
: await decryptPublishes([{ data }], secretKey);
|
||||
|
||||
const messageData = response[0];
|
||||
return messageData.decryptedData;
|
||||
@ -115,7 +113,7 @@ export const Thread = ({
|
||||
secretKey,
|
||||
getSecretKey,
|
||||
updateThreadActivityCurrentThread,
|
||||
isPrivate
|
||||
isPrivate,
|
||||
}: ThreadProps) => {
|
||||
const [tempPublishedList, setTempPublishedList] = useState([]);
|
||||
const [messages, setMessages] = useState<any[]>([]);
|
||||
@ -129,7 +127,7 @@ export const Thread = ({
|
||||
|
||||
// Update: Use a new ref for the scrollable container
|
||||
const threadContainerRef = useRef(null);
|
||||
const threadBeginningRef = useRef(null)
|
||||
const threadBeginningRef = useRef(null);
|
||||
// New state variables
|
||||
const [showScrollButton, setShowScrollButton] = useState(false);
|
||||
const [isAtBottom, setIsAtBottom] = useState(false);
|
||||
@ -140,7 +138,7 @@ export const Thread = ({
|
||||
const dataPublishes = useRef({});
|
||||
|
||||
const getSavedData = useCallback(async (groupId) => {
|
||||
const res = await getDataPublishesFunc(groupId, "thmsg");
|
||||
const res = await getDataPublishesFunc(groupId, 'thmsg');
|
||||
dataPublishes.current = res || {};
|
||||
}, []);
|
||||
|
||||
@ -159,14 +157,17 @@ export const Thread = ({
|
||||
|
||||
const getIndividualMsg = async (message: any) => {
|
||||
try {
|
||||
const responseDataMessage = await getEncryptedResource({
|
||||
identifier: message.identifier,
|
||||
name: message.name,
|
||||
secretKey,
|
||||
resource: message,
|
||||
groupId: groupInfo?.groupId,
|
||||
dataPublishes: dataPublishes.current,
|
||||
}, isPrivate);
|
||||
const responseDataMessage = await getEncryptedResource(
|
||||
{
|
||||
identifier: message.identifier,
|
||||
name: message.name,
|
||||
secretKey,
|
||||
resource: message,
|
||||
groupId: groupInfo?.groupId,
|
||||
dataPublishes: dataPublishes.current,
|
||||
},
|
||||
isPrivate
|
||||
);
|
||||
|
||||
if (responseDataMessage?.error) {
|
||||
const fullObject = {
|
||||
@ -201,7 +202,7 @@ export const Thread = ({
|
||||
try {
|
||||
let threadId = currentThread.threadId;
|
||||
|
||||
const keyTemp = "thread-post";
|
||||
const keyTemp = 'thread-post';
|
||||
const getTempAnnouncements = await getTempPublish();
|
||||
|
||||
if (getTempAnnouncements?.[keyTemp]) {
|
||||
@ -232,10 +233,10 @@ export const Thread = ({
|
||||
const identifier = `thmsg-${threadId}`;
|
||||
let url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&prefix=true`;
|
||||
if (!isReverse) {
|
||||
url = url + "&reverse=false";
|
||||
url = url + '&reverse=false';
|
||||
}
|
||||
if (isReverse) {
|
||||
url = url + "&reverse=true";
|
||||
url = url + '&reverse=true';
|
||||
}
|
||||
if (after) {
|
||||
url = url + `&after=${after}`;
|
||||
@ -245,9 +246,9 @@ export const Thread = ({
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const responseData = await response.json();
|
||||
@ -263,14 +264,13 @@ export const Thread = ({
|
||||
setMessages(fullArrayMsg);
|
||||
if (before === null && after === null && isReverse) {
|
||||
setTimeout(() => {
|
||||
containerRef.current.scrollIntoView({ behavior: "smooth" });
|
||||
containerRef.current.scrollIntoView({ behavior: 'smooth' });
|
||||
}, 300);
|
||||
}
|
||||
if(after || before === null && after === null && !isReverse){
|
||||
if (after || (before === null && after === null && !isReverse)) {
|
||||
setTimeout(() => {
|
||||
threadBeginningRef.current.scrollIntoView();
|
||||
}, 100);
|
||||
|
||||
}
|
||||
|
||||
if (fullArrayMsg.length === 0) {
|
||||
@ -282,9 +282,9 @@ export const Thread = ({
|
||||
fullArrayMsg[0].created
|
||||
}`;
|
||||
const responseNewer = await fetch(urlNewer, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const responseDataNewer = await responseNewer.json();
|
||||
@ -300,9 +300,9 @@ export const Thread = ({
|
||||
fullArrayMsg[fullArrayMsg.length - 1].created
|
||||
}`;
|
||||
const responseOlder = await fetch(urlOlder, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const responseDataOlder = await responseOlder.json();
|
||||
@ -316,7 +316,7 @@ export const Thread = ({
|
||||
updateThreadActivityCurrentThread();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
console.log('error', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
getSavedData(groupId);
|
||||
@ -325,9 +325,21 @@ export const Thread = ({
|
||||
[messages, secretKey]
|
||||
);
|
||||
const getMessages = React.useCallback(async () => {
|
||||
if (!currentThread || (!secretKey && isPrivate) || !groupInfo?.groupId || isPrivate === null) return;
|
||||
if (
|
||||
!currentThread ||
|
||||
(!secretKey && isPrivate) ||
|
||||
!groupInfo?.groupId ||
|
||||
isPrivate === null
|
||||
)
|
||||
return;
|
||||
await getMailMessages(currentThread, null, null, false, groupInfo?.groupId);
|
||||
}, [getMailMessages, currentThread, secretKey, groupInfo?.groupId, isPrivate]);
|
||||
}, [
|
||||
getMailMessages,
|
||||
currentThread,
|
||||
secretKey,
|
||||
groupInfo?.groupId,
|
||||
isPrivate,
|
||||
]);
|
||||
const firstMount = useRef(false);
|
||||
|
||||
const saveTimestamp = useCallback((currentThread: any, username?: string) => {
|
||||
@ -339,7 +351,7 @@ export const Thread = ({
|
||||
return;
|
||||
const threadIdForLocalStorage = `qmail_threads_${currentThread?.threadData?.groupId}_${currentThread?.threadId}`;
|
||||
const threads = JSON.parse(
|
||||
localStorage.getItem(`qmail_threads_viewedtimestamp_${username}`) || "{}"
|
||||
localStorage.getItem(`qmail_threads_viewedtimestamp_${username}`) || '{}'
|
||||
);
|
||||
// Convert to an array of objects with identifier and all fields
|
||||
let dataArray = Object.entries(threads).map(([identifier, value]) => ({
|
||||
@ -382,8 +394,8 @@ export const Thread = ({
|
||||
if (currentThreadRef.current?.threadId !== currentThread?.threadId) {
|
||||
firstMount.current = false;
|
||||
}
|
||||
if(!secretKey && isPrivate) return
|
||||
if (currentThread && !firstMount.current && isPrivate !== null) {
|
||||
if (!secretKey && isPrivate) return;
|
||||
if (currentThread && !firstMount.current && isPrivate !== null) {
|
||||
getMessagesMiddleware();
|
||||
}
|
||||
}, [currentThread, secretKey, isPrivate]);
|
||||
@ -402,9 +414,9 @@ export const Thread = ({
|
||||
const identifier = `thmsg-${threadId}`;
|
||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&offset=${0}&reverse=true&prefix=true`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const responseData = await response.json();
|
||||
@ -469,17 +481,17 @@ export const Thread = ({
|
||||
|
||||
const threadFetchModeFunc = (e) => {
|
||||
const mode = e.detail?.mode;
|
||||
if (mode === "last-page") {
|
||||
if (mode === 'last-page') {
|
||||
getMailMessages(currentThread, null, null, true, groupInfo?.groupId);
|
||||
}
|
||||
firstMount.current = true;
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
subscribeToEvent("threadFetchMode", threadFetchModeFunc);
|
||||
subscribeToEvent('threadFetchMode', threadFetchModeFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("threadFetchMode", threadFetchModeFunc);
|
||||
unsubscribeFromEvent('threadFetchMode', threadFetchModeFunc);
|
||||
};
|
||||
}, []);
|
||||
|
||||
@ -526,11 +538,11 @@ export const Thread = ({
|
||||
handleScroll();
|
||||
}, 400);
|
||||
|
||||
container.addEventListener("scroll", handleScroll);
|
||||
container.addEventListener('scroll', handleScroll);
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
container.removeEventListener("scroll", handleScroll);
|
||||
container.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, [messages]);
|
||||
|
||||
@ -540,9 +552,9 @@ export const Thread = ({
|
||||
if (!container) return;
|
||||
|
||||
if (isAtBottom) {
|
||||
container.scrollTo({ top: 0, behavior: "smooth" }); // Scroll to top
|
||||
container.scrollTo({ top: 0, behavior: 'smooth' }); // Scroll to top
|
||||
} else {
|
||||
container.scrollTo({ top: container.scrollHeight, behavior: "smooth" }); // Scroll to bottom
|
||||
container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); // Scroll to bottom
|
||||
}
|
||||
};
|
||||
|
||||
@ -550,19 +562,19 @@ export const Thread = ({
|
||||
return (
|
||||
<GroupContainer
|
||||
sx={{
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflow: "hidden",
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
// Removed the ref from here since the scrollable area has changed
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
flexShrink: 0, // Corrected property name
|
||||
}}
|
||||
>
|
||||
@ -583,16 +595,16 @@ export const Thread = ({
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: isMobile ? "45px" : "35px",
|
||||
alignItems: "center",
|
||||
padding: isMobile && "5px",
|
||||
display: 'flex',
|
||||
gap: isMobile ? '45px' : '35px',
|
||||
alignItems: 'center',
|
||||
padding: isMobile && '5px',
|
||||
}}
|
||||
>
|
||||
<ShowMessageReturnButton
|
||||
sx={{
|
||||
padding: isMobile && "5px",
|
||||
minWidth: isMobile && "50px",
|
||||
padding: isMobile && '5px',
|
||||
minWidth: isMobile && '50px',
|
||||
}}
|
||||
onClick={() => {
|
||||
setMessages([]);
|
||||
@ -608,9 +620,9 @@ export const Thread = ({
|
||||
<ButtonBase onClick={scrollToPosition}>
|
||||
<ArrowUpwardIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
cursor: "pointer",
|
||||
fontSize: isMobile ? "28px" : "36px",
|
||||
color: 'white',
|
||||
cursor: 'pointer',
|
||||
fontSize: isMobile ? '28px' : '36px',
|
||||
}}
|
||||
/>
|
||||
</ButtonBase>
|
||||
@ -618,9 +630,9 @@ export const Thread = ({
|
||||
<ButtonBase onClick={scrollToPosition}>
|
||||
<ArrowDownwardIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
cursor: "pointer",
|
||||
fontSize: isMobile ? "28px" : "36px",
|
||||
color: 'white',
|
||||
cursor: 'pointer',
|
||||
fontSize: isMobile ? '28px' : '36px',
|
||||
}}
|
||||
/>
|
||||
</ButtonBase>
|
||||
@ -631,45 +643,45 @@ export const Thread = ({
|
||||
<ThreadContainerFullWidth
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
overflow: "auto",
|
||||
overflow: 'auto',
|
||||
}}
|
||||
ref={threadContainerRef} // Updated ref attached here
|
||||
>
|
||||
<div ref={threadBeginningRef}/>
|
||||
<div ref={threadBeginningRef} />
|
||||
<ThreadContainer>
|
||||
<Spacer height={isMobile ? "10px" : "30px"} />
|
||||
<Spacer height={isMobile ? '10px' : '30px'} />
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<GroupNameP
|
||||
sx={{
|
||||
fontSize: isMobile && "18px",
|
||||
fontSize: isMobile && '18px',
|
||||
}}
|
||||
>
|
||||
{currentThread?.threadData?.title}
|
||||
</GroupNameP>
|
||||
</Box>
|
||||
<Spacer height={"15px"} />
|
||||
<Spacer height={'15px'} />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
gap: "5px",
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
gap: '5px',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && "5px",
|
||||
fontSize: isMobile && "14px",
|
||||
textTransformation: "capitalize",
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
getMailMessages(
|
||||
@ -687,9 +699,9 @@ export const Thread = ({
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && "5px",
|
||||
fontSize: isMobile && "14px",
|
||||
textTransformation: "capitalize",
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
getMailMessages(
|
||||
@ -707,9 +719,9 @@ export const Thread = ({
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && "5px",
|
||||
fontSize: isMobile && "14px",
|
||||
textTransformation: "capitalize",
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
getMailMessages(
|
||||
@ -727,9 +739,9 @@ export const Thread = ({
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && "5px",
|
||||
fontSize: isMobile && "14px",
|
||||
textTransformation: "capitalize",
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
getMailMessages(
|
||||
@ -746,35 +758,34 @@ export const Thread = ({
|
||||
Last
|
||||
</Button>
|
||||
</Box>
|
||||
<Spacer height={isMobile ? "10px" : "30px"} />
|
||||
<Spacer height={isMobile ? '10px' : '30px'} />
|
||||
{combinedListTempAndReal.map((message, index, list) => {
|
||||
let fullMessage = message;
|
||||
|
||||
if (hashMapMailMessages[message?.identifier]) {
|
||||
fullMessage = hashMapMailMessages[message.identifier];
|
||||
|
||||
|
||||
if (fullMessage?.error) {
|
||||
return (
|
||||
<SingleThreadParent
|
||||
sx={{
|
||||
height: "auto",
|
||||
height: 'auto',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
style={{
|
||||
width: "100%",
|
||||
borderRadius: "8px",
|
||||
overflow: "hidden",
|
||||
position: "relative",
|
||||
flexDirection: "column",
|
||||
width: '100%',
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
gap: "10px",
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<WrapperUserAction
|
||||
@ -784,8 +795,8 @@ export const Thread = ({
|
||||
>
|
||||
<Avatar
|
||||
sx={{
|
||||
height: "50px",
|
||||
width: "50px",
|
||||
height: '50px',
|
||||
width: '50px',
|
||||
}}
|
||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||
message?.name
|
||||
@ -812,23 +823,22 @@ export const Thread = ({
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
color: "white",
|
||||
fontSize: '18px',
|
||||
color: 'white',
|
||||
}}
|
||||
>
|
||||
{fullMessage?.error}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
</Box>
|
||||
</SingleThreadParent>
|
||||
);
|
||||
@ -855,23 +865,23 @@ export const Thread = ({
|
||||
return (
|
||||
<SingleThreadParent
|
||||
sx={{
|
||||
height: "auto",
|
||||
height: 'auto',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
style={{
|
||||
width: "100%",
|
||||
borderRadius: "8px",
|
||||
overflow: "hidden",
|
||||
position: "relative",
|
||||
flexDirection: "column",
|
||||
width: '100%',
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
gap: "10px",
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<WrapperUserAction
|
||||
@ -881,8 +891,8 @@ export const Thread = ({
|
||||
>
|
||||
<Avatar
|
||||
sx={{
|
||||
height: "50px",
|
||||
width: "50px",
|
||||
height: '50px',
|
||||
width: '50px',
|
||||
}}
|
||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||
message?.name
|
||||
@ -909,37 +919,36 @@ export const Thread = ({
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<CustomLoader />
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "18px",
|
||||
color: "white",
|
||||
fontSize: '18px',
|
||||
color: 'white',
|
||||
}}
|
||||
>
|
||||
Downloading from QDN
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
</Box>
|
||||
</SingleThreadParent>
|
||||
);
|
||||
})}
|
||||
|
||||
|
||||
{!hasLastPage && !isLoading && (
|
||||
<>
|
||||
<Spacer height="20px" />
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
@ -955,7 +964,7 @@ export const Thread = ({
|
||||
);
|
||||
}}
|
||||
sx={{
|
||||
color: "white",
|
||||
color: 'white',
|
||||
}}
|
||||
>
|
||||
Refetch page
|
||||
@ -964,112 +973,113 @@ export const Thread = ({
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
<Box sx={{
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
visibility: messages?.length > 4 ? 'visible' : 'hidden'
|
||||
}}>
|
||||
<Spacer height="30px" />
|
||||
<Box
|
||||
visibility: messages?.length > 4 ? 'visible' : 'hidden',
|
||||
}}
|
||||
>
|
||||
<Spacer height="30px" />
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
gap: '5px',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
sx={{
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
gap: "5px",
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
getMailMessages(
|
||||
currentThread,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
groupInfo?.groupId
|
||||
);
|
||||
}}
|
||||
disabled={!hasFirstPage}
|
||||
variant="contained"
|
||||
>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && "5px",
|
||||
fontSize: isMobile && "14px",
|
||||
textTransformation: "capitalize",
|
||||
}}
|
||||
onClick={() => {
|
||||
getMailMessages(
|
||||
currentThread,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
groupInfo?.groupId
|
||||
);
|
||||
}}
|
||||
disabled={!hasFirstPage}
|
||||
variant="contained"
|
||||
>
|
||||
First
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && "5px",
|
||||
fontSize: isMobile && "14px",
|
||||
textTransformation: "capitalize",
|
||||
}}
|
||||
onClick={() => {
|
||||
getMailMessages(
|
||||
currentThread,
|
||||
messages[0].created,
|
||||
null,
|
||||
false,
|
||||
groupInfo?.groupId
|
||||
);
|
||||
}}
|
||||
disabled={!hasPreviousPage}
|
||||
variant="contained"
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && "5px",
|
||||
fontSize: isMobile && "14px",
|
||||
textTransformation: "capitalize",
|
||||
}}
|
||||
onClick={() => {
|
||||
getMailMessages(
|
||||
currentThread,
|
||||
null,
|
||||
messages[messages.length - 1].created,
|
||||
false,
|
||||
groupInfo?.groupId
|
||||
);
|
||||
}}
|
||||
disabled={!hasNextPage}
|
||||
variant="contained"
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && "5px",
|
||||
fontSize: isMobile && "14px",
|
||||
textTransformation: "capitalize",
|
||||
}}
|
||||
onClick={() => {
|
||||
getMailMessages(
|
||||
currentThread,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
groupInfo?.groupId
|
||||
);
|
||||
}}
|
||||
disabled={!hasLastPage}
|
||||
variant="contained"
|
||||
>
|
||||
Last
|
||||
</Button>
|
||||
</Box>
|
||||
<Spacer height="30px" />
|
||||
First
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
getMailMessages(
|
||||
currentThread,
|
||||
messages[0].created,
|
||||
null,
|
||||
false,
|
||||
groupInfo?.groupId
|
||||
);
|
||||
}}
|
||||
disabled={!hasPreviousPage}
|
||||
variant="contained"
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
getMailMessages(
|
||||
currentThread,
|
||||
null,
|
||||
messages[messages.length - 1].created,
|
||||
false,
|
||||
groupInfo?.groupId
|
||||
);
|
||||
}}
|
||||
disabled={!hasNextPage}
|
||||
variant="contained"
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
getMailMessages(
|
||||
currentThread,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
groupInfo?.groupId
|
||||
);
|
||||
}}
|
||||
disabled={!hasLastPage}
|
||||
variant="contained"
|
||||
>
|
||||
Last
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<div ref={containerRef} />
|
||||
<Spacer height="30px" />
|
||||
</Box>
|
||||
|
||||
<div ref={containerRef} />
|
||||
</ThreadContainer>
|
||||
</ThreadContainerFullWidth>
|
||||
<LoadingSnackbar
|
||||
open={isLoading}
|
||||
info={{
|
||||
message: "Loading posts... please wait.",
|
||||
message: 'Loading posts... please wait.',
|
||||
}}
|
||||
/>
|
||||
</GroupContainer>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -19,7 +19,7 @@ export const useBlockedAddresses = () => {
|
||||
const isUserBlocked = useCallback((address, name)=> {
|
||||
try {
|
||||
if(!address) return false
|
||||
if(userBlockedRef.current[address] || userNamesBlockedRef.current[name]) return true
|
||||
if(userBlockedRef.current[address]) return true
|
||||
return false
|
||||
|
||||
|
||||
@ -90,43 +90,13 @@ export const useBlockedAddresses = () => {
|
||||
}, [])
|
||||
|
||||
const removeBlockFromList = useCallback(async (address, name)=> {
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
type: 'remove',
|
||||
items: name ? [name] : [address],
|
||||
listName: name ? 'blockedNames' : 'blockedAddresses'
|
||||
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
if(!name){
|
||||
const copyObject = {...userBlockedRef.current}
|
||||
delete copyObject[address]
|
||||
userBlockedRef.current = copyObject
|
||||
} else {
|
||||
const copyObject = {...userNamesBlockedRef.current}
|
||||
delete copyObject[name]
|
||||
userNamesBlockedRef.current = copyObject
|
||||
}
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
});
|
||||
})
|
||||
if(name && userBlockedRef.current[address]){
|
||||
if(name){
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
type: 'remove',
|
||||
items: !name ? [name] : [address],
|
||||
listName: !name ? 'blockedNames' : 'blockedAddresses'
|
||||
items: [name] ,
|
||||
listName: 'blockedNames'
|
||||
|
||||
})
|
||||
.then((response) => {
|
||||
@ -134,9 +104,12 @@ export const useBlockedAddresses = () => {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
const copyObject = {...userBlockedRef.current}
|
||||
delete copyObject[address]
|
||||
userBlockedRef.current = copyObject
|
||||
|
||||
const copyObject = {...userNamesBlockedRef.current}
|
||||
delete copyObject[name]
|
||||
userNamesBlockedRef.current = copyObject
|
||||
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
@ -145,42 +118,95 @@ export const useBlockedAddresses = () => {
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
if(address){
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
type: 'remove',
|
||||
items: [address],
|
||||
listName: 'blockedAddresses'
|
||||
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
|
||||
const copyObject = {...userBlockedRef.current}
|
||||
delete copyObject[address]
|
||||
userBlockedRef.current = copyObject
|
||||
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}, [])
|
||||
|
||||
const addToBlockList = useCallback(async (address, name)=> {
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
type: 'add',
|
||||
items: name ? [name] : [address],
|
||||
listName: name ? 'blockedNames' : 'blockedAddresses'
|
||||
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
if(name){
|
||||
|
||||
const copyObject = {...userNamesBlockedRef.current}
|
||||
copyObject[name] = true
|
||||
userNamesBlockedRef.current = copyObject
|
||||
}else {
|
||||
const copyObject = {...userBlockedRef.current}
|
||||
copyObject[address] = true
|
||||
userBlockedRef.current = copyObject
|
||||
|
||||
}
|
||||
if(name){
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
res(response);
|
||||
}
|
||||
type: 'add',
|
||||
items: [name],
|
||||
listName: 'blockedNames'
|
||||
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
const copyObject = {...userNamesBlockedRef.current}
|
||||
copyObject[name] = true
|
||||
userNamesBlockedRef.current = copyObject
|
||||
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
});
|
||||
})
|
||||
}
|
||||
if(address){
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("listActions", {
|
||||
|
||||
type: 'add',
|
||||
items: [address],
|
||||
listName: 'blockedAddresses'
|
||||
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.error) {
|
||||
rej(response?.message);
|
||||
return;
|
||||
} else {
|
||||
|
||||
const copyObject = {...userBlockedRef.current}
|
||||
copyObject[address] = true
|
||||
userBlockedRef.current = copyObject
|
||||
|
||||
res(response);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed qortalRequest", error);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
}, [])
|
||||
|
||||
return {
|
||||
|
@ -13,29 +13,29 @@ import {
|
||||
InputLabel,
|
||||
Snackbar,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
} from '@mui/material';
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import { MyContext, getBaseApiReact } from "../../App";
|
||||
} from 'react';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
import {
|
||||
executeEvent,
|
||||
subscribeToEvent,
|
||||
unsubscribeFromEvent,
|
||||
} from "../../utils/events";
|
||||
import { getFee, getNameOrAddress } from "../../background";
|
||||
import CopyToClipboard from "react-copy-to-clipboard";
|
||||
import { AddressBox } from "../../App-styles";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import Copy from "../../assets/svgs/Copy.svg";
|
||||
import { Loader } from "../Loader";
|
||||
import { FidgetSpinner } from "react-loader-spinner";
|
||||
import { useModal } from "../../common/useModal";
|
||||
} from '../../utils/events';
|
||||
import { getFee, getNameOrAddress } from '../../background';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import { AddressBox } from '../../styles/App-styles';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import Copy from '../../assets/svgs/Copy.svg';
|
||||
import { Loader } from '../Loader';
|
||||
import { FidgetSpinner } from 'react-loader-spinner';
|
||||
import { useModal } from '../../common/useModal';
|
||||
|
||||
export const Minting = ({
|
||||
setIsOpenMinting,
|
||||
@ -47,30 +47,30 @@ export const Minting = ({
|
||||
}) => {
|
||||
const [mintingAccounts, setMintingAccounts] = useState([]);
|
||||
const [accountInfo, setAccountInfo] = useState(null);
|
||||
const [rewardSharePublicKey, setRewardSharePublicKey] = useState("");
|
||||
const [mintingKey, setMintingKey] = useState("");
|
||||
const [rewardsharekey, setRewardsharekey] = useState("");
|
||||
const [rewardSharePublicKey, setRewardSharePublicKey] = useState('');
|
||||
const [mintingKey, setMintingKey] = useState('');
|
||||
const [rewardsharekey, setRewardsharekey] = useState('');
|
||||
const [rewardShares, setRewardShares] = useState([]);
|
||||
const [nodeInfos, setNodeInfos] = useState({});
|
||||
const [openSnack, setOpenSnack] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { show: showKey, message } = useModal();
|
||||
const { isShow: isShowNext, onOk, show: showNext } = useModal();
|
||||
const { show: showKey, message } = useModal();
|
||||
const { isShow: isShowNext, onOk, show: showNext } = useModal();
|
||||
|
||||
const [info, setInfo] = useState(null);
|
||||
const [names, setNames] = useState({});
|
||||
const [accountInfos, setAccountInfos] = useState({});
|
||||
const [showWaitDialog, setShowWaitDialog] = useState(false)
|
||||
const [showWaitDialog, setShowWaitDialog] = useState(false);
|
||||
const isPartOfMintingGroup = useMemo(() => {
|
||||
if (groups?.length === 0) return false;
|
||||
return !!groups?.find((item) => item?.groupId?.toString() === "694");
|
||||
return !!groups?.find((item) => item?.groupId?.toString() === '694');
|
||||
}, [groups]);
|
||||
const getMintingAccounts = useCallback(async () => {
|
||||
try {
|
||||
const url = `${getBaseApiReact()}/admin/mintingaccounts`;
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error("network error");
|
||||
throw new Error('network error');
|
||||
}
|
||||
const data = await response.json();
|
||||
setMintingAccounts(data);
|
||||
@ -117,7 +117,7 @@ export const Minting = ({
|
||||
const url = `${getBaseApiReact()}/addresses/${address}`;
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error("network error");
|
||||
throw new Error('network error');
|
||||
}
|
||||
const data = await response.json();
|
||||
if (others) {
|
||||
@ -144,10 +144,10 @@ export const Minting = ({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("refresh-rewardshare-list", refreshRewardShare);
|
||||
subscribeToEvent('refresh-rewardshare-list', refreshRewardShare);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("refresh-rewardshare-list", refreshRewardShare);
|
||||
unsubscribeFromEvent('refresh-rewardshare-list', refreshRewardShare);
|
||||
};
|
||||
}, [myAddress]);
|
||||
|
||||
@ -177,15 +177,15 @@ export const Minting = ({
|
||||
try {
|
||||
const url = `${getBaseApiReact()}/admin/status`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const data = await response.json();
|
||||
setNodeInfos(data);
|
||||
} catch (error) {
|
||||
console.error("Request failed", error);
|
||||
console.error('Request failed', error);
|
||||
}
|
||||
};
|
||||
|
||||
@ -194,11 +194,11 @@ export const Minting = ({
|
||||
const url = `${getBaseApiReact()}/addresses/rewardshares?involving=${address}`;
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error("network error");
|
||||
throw new Error('network error');
|
||||
}
|
||||
const data = await response.json();
|
||||
setRewardShares(data);
|
||||
return data
|
||||
return data;
|
||||
} catch (error) {}
|
||||
}, []);
|
||||
|
||||
@ -208,10 +208,10 @@ export const Minting = ({
|
||||
return await new Promise((res, rej) => {
|
||||
window
|
||||
.sendMessage(
|
||||
"ADMIN_ACTION",
|
||||
'ADMIN_ACTION',
|
||||
|
||||
{
|
||||
type: "addmintingaccount",
|
||||
type: 'addmintingaccount',
|
||||
value: val,
|
||||
},
|
||||
180000,
|
||||
@ -220,7 +220,7 @@ export const Minting = ({
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
res(response);
|
||||
setMintingKey("");
|
||||
setMintingKey('');
|
||||
setTimeout(() => {
|
||||
getMintingAccounts();
|
||||
}, 300);
|
||||
@ -229,13 +229,13 @@ export const Minting = ({
|
||||
rej({ message: response.error });
|
||||
})
|
||||
.catch((error) => {
|
||||
rej({ message: error.message || "An error occurred" });
|
||||
rej({ message: error.message || 'An error occurred' });
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
setInfo({
|
||||
type: "error",
|
||||
message: error?.message || "Unable to add minting account",
|
||||
type: 'error',
|
||||
message: error?.message || 'Unable to add minting account',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
} finally {
|
||||
@ -249,10 +249,10 @@ export const Minting = ({
|
||||
return await new Promise((res, rej) => {
|
||||
window
|
||||
.sendMessage(
|
||||
"ADMIN_ACTION",
|
||||
'ADMIN_ACTION',
|
||||
|
||||
{
|
||||
type: "removemintingaccount",
|
||||
type: 'removemintingaccount',
|
||||
value: val,
|
||||
},
|
||||
180000,
|
||||
@ -270,13 +270,13 @@ export const Minting = ({
|
||||
rej({ message: response.error });
|
||||
})
|
||||
.catch((error) => {
|
||||
rej({ message: error.message || "An error occurred" });
|
||||
rej({ message: error.message || 'An error occurred' });
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
setInfo({
|
||||
type: "error",
|
||||
message: error?.message || "Unable to remove minting account",
|
||||
type: 'error',
|
||||
message: error?.message || 'Unable to remove minting account',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
} finally {
|
||||
@ -285,14 +285,14 @@ export const Minting = ({
|
||||
}, []);
|
||||
|
||||
const createRewardShare = useCallback(async (publicKey, recipient) => {
|
||||
const fee = await getFee("REWARD_SHARE");
|
||||
const fee = await getFee('REWARD_SHARE');
|
||||
await show({
|
||||
message: "Would you like to perform an REWARD_SHARE transaction?",
|
||||
publishFee: fee.fee + " QORT",
|
||||
message: 'Would you like to perform an REWARD_SHARE transaction?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
return await new Promise((res, rej) => {
|
||||
window
|
||||
.sendMessage("createRewardShare", {
|
||||
.sendMessage('createRewardShare', {
|
||||
recipientPublicKey: publicKey,
|
||||
})
|
||||
.then((response) => {
|
||||
@ -301,7 +301,7 @@ export const Minting = ({
|
||||
{
|
||||
recipient,
|
||||
...response,
|
||||
type: "add-rewardShare",
|
||||
type: 'add-rewardShare',
|
||||
label: `Add rewardshare: awaiting confirmation`,
|
||||
labelDone: `Add rewardshare: success!`,
|
||||
done: false,
|
||||
@ -314,7 +314,7 @@ export const Minting = ({
|
||||
rej({ message: response.error });
|
||||
})
|
||||
.catch((error) => {
|
||||
rej({ message: error.message || "An error occurred" });
|
||||
rej({ message: error.message || 'An error occurred' });
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
@ -322,7 +322,7 @@ export const Minting = ({
|
||||
const getRewardSharePrivateKey = useCallback(async (publicKey) => {
|
||||
return await new Promise((res, rej) => {
|
||||
window
|
||||
.sendMessage("getRewardSharePrivateKey", {
|
||||
.sendMessage('getRewardSharePrivateKey', {
|
||||
recipientPublicKey: publicKey,
|
||||
})
|
||||
.then((response) => {
|
||||
@ -333,7 +333,7 @@ export const Minting = ({
|
||||
rej({ message: response.error });
|
||||
})
|
||||
.catch((error) => {
|
||||
rej({ message: error.message || "An error occurred" });
|
||||
rej({ message: error.message || 'An error occurred' });
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
@ -341,26 +341,24 @@ export const Minting = ({
|
||||
const waitUntilRewardShareIsConfirmed = async (timeoutMs = 600000) => {
|
||||
const pollingInterval = 30000;
|
||||
const startTime = Date.now();
|
||||
|
||||
|
||||
const sleep = (ms) => new Promise((res) => setTimeout(res, ms));
|
||||
|
||||
|
||||
while (Date.now() - startTime < timeoutMs) {
|
||||
|
||||
const rewardShares = await getRewardShares(myAddress);
|
||||
const findRewardShare = rewardShares?.find(
|
||||
(item) =>
|
||||
item?.recipient === myAddress && item?.mintingAccount === myAddress
|
||||
);
|
||||
|
||||
if (findRewardShare) {
|
||||
return true; // Exit early if found
|
||||
}
|
||||
|
||||
|
||||
const rewardShares = await getRewardShares(myAddress);
|
||||
const findRewardShare = rewardShares?.find(
|
||||
(item) =>
|
||||
item?.recipient === myAddress && item?.mintingAccount === myAddress
|
||||
);
|
||||
|
||||
if (findRewardShare) {
|
||||
return true; // Exit early if found
|
||||
}
|
||||
|
||||
await sleep(pollingInterval); // Wait before the next poll
|
||||
}
|
||||
|
||||
throw new Error("Timeout waiting for reward share confirmation");
|
||||
|
||||
throw new Error('Timeout waiting for reward share confirmation');
|
||||
};
|
||||
|
||||
const startMinting = async () => {
|
||||
@ -377,23 +375,22 @@ export const Minting = ({
|
||||
addMintingAccount(privateRewardShare);
|
||||
} else {
|
||||
await createRewardShare(accountInfo?.publicKey, myAddress);
|
||||
setShowWaitDialog(true)
|
||||
await waitUntilRewardShareIsConfirmed()
|
||||
setShowWaitDialog(true);
|
||||
await waitUntilRewardShareIsConfirmed();
|
||||
await showNext({
|
||||
message: ''
|
||||
})
|
||||
message: '',
|
||||
});
|
||||
const privateRewardShare = await getRewardSharePrivateKey(
|
||||
accountInfo?.publicKey
|
||||
);
|
||||
setShowWaitDialog(false)
|
||||
setShowWaitDialog(false);
|
||||
addMintingAccount(privateRewardShare);
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
setShowWaitDialog(false)
|
||||
setShowWaitDialog(false);
|
||||
setInfo({
|
||||
type: "error",
|
||||
message: error?.message || "Unable to start minting",
|
||||
type: 'error',
|
||||
message: error?.message || 'Unable to start minting',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
} finally {
|
||||
@ -412,13 +409,13 @@ export const Minting = ({
|
||||
const url = `${getBaseApiReact()}/groups/member/${address}`;
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
return !!data?.find((grp) => grp?.groupId?.toString() === "694");
|
||||
return !!data?.find((grp) => grp?.groupId?.toString() === '694');
|
||||
};
|
||||
|
||||
const removeRewardShare = useCallback(async (rewardShare) => {
|
||||
return await new Promise((res, rej) => {
|
||||
window
|
||||
.sendMessage("removeRewardShare", {
|
||||
.sendMessage('removeRewardShare', {
|
||||
rewardShareKeyPairPublicKey: rewardShare.rewardSharePublicKey,
|
||||
recipient: rewardShare.recipient,
|
||||
percentageShare: -1,
|
||||
@ -430,7 +427,7 @@ export const Minting = ({
|
||||
{
|
||||
...rewardShare,
|
||||
...response,
|
||||
type: "remove-rewardShare",
|
||||
type: 'remove-rewardShare',
|
||||
label: `Remove rewardshare: awaiting confirmation`,
|
||||
labelDone: `Remove rewardshare: success!`,
|
||||
done: false,
|
||||
@ -442,7 +439,7 @@ export const Minting = ({
|
||||
rej({ message: response.error });
|
||||
})
|
||||
.catch((error) => {
|
||||
rej({ message: error.message || "An error occurred" });
|
||||
rej({ message: error.message || 'An error occurred' });
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
@ -454,8 +451,8 @@ export const Minting = ({
|
||||
const privateRewardShare = await removeRewardShare(rewardShare);
|
||||
} catch (error) {
|
||||
setInfo({
|
||||
type: "error",
|
||||
message: error?.message || "Unable to remove reward share",
|
||||
type: 'error',
|
||||
message: error?.message || 'Unable to remove reward share',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
} finally {
|
||||
@ -468,9 +465,9 @@ export const Minting = ({
|
||||
setIsLoading(true);
|
||||
const confirmReceiver = await getNameOrAddress(receiver);
|
||||
if (confirmReceiver.error)
|
||||
throw new Error("Invalid receiver address or name");
|
||||
throw new Error('Invalid receiver address or name');
|
||||
const isInMinterGroup = await checkIfMinterGroup(confirmReceiver);
|
||||
if (!isInMinterGroup) throw new Error("Account not in Minter Group");
|
||||
if (!isInMinterGroup) throw new Error('Account not in Minter Group');
|
||||
const publicKey = await getPublicKeyFromAddress(confirmReceiver);
|
||||
const findRewardShare = rewardShares?.find(
|
||||
(item) =>
|
||||
@ -487,8 +484,8 @@ export const Minting = ({
|
||||
}
|
||||
} catch (error) {
|
||||
setInfo({
|
||||
type: "error",
|
||||
message: error?.message || "Unable to create reward share",
|
||||
type: 'error',
|
||||
message: error?.message || 'Unable to create reward share',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
} finally {
|
||||
@ -550,11 +547,9 @@ export const Minting = ({
|
||||
(accountInfo?.blocksMinted + accountInfo?.blocksMintedAdjustment);
|
||||
|
||||
let countBlocksString = countBlocks.toString();
|
||||
return "" + countBlocksString;
|
||||
return '' + countBlocksString;
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={true}
|
||||
@ -562,19 +557,19 @@ export const Minting = ({
|
||||
fullWidth
|
||||
fullScreen
|
||||
sx={{
|
||||
"& .MuiDialog-paper": {
|
||||
'& .MuiDialog-paper': {
|
||||
margin: 0,
|
||||
maxWidth: "100%",
|
||||
width: "100%",
|
||||
height: "100vh",
|
||||
overflow: "hidden", // Prevent scrollbars
|
||||
maxWidth: '100%',
|
||||
width: '100%',
|
||||
height: '100vh',
|
||||
overflow: 'hidden', // Prevent scrollbars
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">{"Manage your minting"}</DialogTitle>
|
||||
<DialogTitle id="alert-dialog-title">{'Manage your minting'}</DialogTitle>
|
||||
<IconButton
|
||||
sx={{
|
||||
position: "absolute",
|
||||
position: 'absolute',
|
||||
right: 8,
|
||||
top: 8,
|
||||
}}
|
||||
@ -586,20 +581,20 @@ export const Minting = ({
|
||||
</IconButton>
|
||||
<DialogContent
|
||||
sx={{
|
||||
position: "relative",
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{isLoading && (
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<FidgetSpinner
|
||||
@ -614,8 +609,8 @@ export const Minting = ({
|
||||
)}
|
||||
<Card
|
||||
sx={{
|
||||
backgroundColor: "var(--bg-2)",
|
||||
padding: "10px",
|
||||
backgroundColor: 'var(--bg-2)',
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
<Typography>Account: {handleNames(accountInfo?.address)}</Typography>
|
||||
@ -631,11 +626,11 @@ export const Minting = ({
|
||||
{isPartOfMintingGroup && !accountIsMinting && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "5px",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
gap: '5px',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
@ -645,15 +640,15 @@ export const Minting = ({
|
||||
}}
|
||||
disabled={mintingAccounts?.length > 1}
|
||||
sx={{
|
||||
backgroundColor: "var(--green)",
|
||||
color: "black",
|
||||
fontWeight: "bold",
|
||||
backgroundColor: 'var(--green)',
|
||||
color: 'black',
|
||||
fontWeight: 'bold',
|
||||
opacity: 0.7,
|
||||
maxWidth: "90%",
|
||||
width: "200px",
|
||||
"&:hover": {
|
||||
backgroundColor: "var(--green)",
|
||||
color: "black",
|
||||
maxWidth: '90%',
|
||||
width: '200px',
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--green)',
|
||||
color: 'black',
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
@ -675,16 +670,16 @@ export const Minting = ({
|
||||
)}
|
||||
<Card
|
||||
sx={{
|
||||
backgroundColor: "var(--bg-2)",
|
||||
padding: "10px",
|
||||
backgroundColor: 'var(--bg-2)',
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
{accountIsMinting && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "5px",
|
||||
flexDirection: "column",
|
||||
display: 'flex',
|
||||
gap: '5px',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<Typography>
|
||||
@ -698,9 +693,9 @@ export const Minting = ({
|
||||
<Box
|
||||
key={acct?.mintingAccount}
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
flexDirection: "column",
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<Typography>
|
||||
@ -709,15 +704,15 @@ export const Minting = ({
|
||||
<Button
|
||||
size="small"
|
||||
sx={{
|
||||
backgroundColor: "var(--danger)",
|
||||
color: "black",
|
||||
fontWeight: "bold",
|
||||
backgroundColor: 'var(--danger)',
|
||||
color: 'black',
|
||||
fontWeight: 'bold',
|
||||
opacity: 0.7,
|
||||
maxWidth: "90%",
|
||||
width: "200px",
|
||||
"&:hover": {
|
||||
backgroundColor: "var(--danger)",
|
||||
color: "black",
|
||||
maxWidth: '90%',
|
||||
width: '200px',
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--danger)',
|
||||
color: 'black',
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
@ -745,17 +740,17 @@ export const Minting = ({
|
||||
{!isPartOfMintingGroup && (
|
||||
<Card
|
||||
sx={{
|
||||
backgroundColor: "var(--bg-2)",
|
||||
padding: "10px",
|
||||
backgroundColor: 'var(--bg-2)',
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "5px",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
display: 'flex',
|
||||
gap: '5px',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography>
|
||||
@ -768,22 +763,22 @@ export const Minting = ({
|
||||
<Button
|
||||
size="small"
|
||||
sx={{
|
||||
backgroundColor: "var(--green)",
|
||||
color: "black",
|
||||
fontWeight: "bold",
|
||||
backgroundColor: 'var(--green)',
|
||||
color: 'black',
|
||||
fontWeight: 'bold',
|
||||
opacity: 0.7,
|
||||
|
||||
"&:hover": {
|
||||
backgroundColor: "var(--green)",
|
||||
color: "black",
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--green)',
|
||||
color: 'black',
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
onClick={() => {
|
||||
executeEvent("addTab", {
|
||||
data: { service: "APP", name: "q-mintership" },
|
||||
executeEvent('addTab', {
|
||||
data: { service: 'APP', name: 'q-mintership' },
|
||||
});
|
||||
executeEvent("open-apps-mode", {});
|
||||
executeEvent('open-apps-mode', {});
|
||||
setIsOpenMinting(false);
|
||||
}}
|
||||
variant="contained"
|
||||
@ -801,29 +796,32 @@ export const Minting = ({
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{isShowNext ? "Confirmed" : "Please Wait"}
|
||||
{isShowNext ? 'Confirmed' : 'Please Wait'}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
{!isShowNext && (
|
||||
<Typography>
|
||||
Confirming creation of rewardshare on chain. Please be patient, this could take up to 90 seconds.
|
||||
</Typography>
|
||||
Confirming creation of rewardshare on chain. Please be
|
||||
patient, this could take up to 90 seconds.
|
||||
</Typography>
|
||||
)}
|
||||
{isShowNext && (
|
||||
<Typography>
|
||||
Rewardshare confirmed. Please click Next.
|
||||
</Typography>
|
||||
Rewardshare confirmed. Please click Next.
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button disabled={!isShowNext} variant="contained" onClick={onOk} autoFocus>
|
||||
|
||||
<DialogActions>
|
||||
<Button
|
||||
disabled={!isShowNext}
|
||||
variant="contained"
|
||||
onClick={onOk}
|
||||
autoFocus
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</DialogActions>
|
||||
|
||||
|
||||
</Dialog>
|
||||
)}
|
||||
</DialogContent>
|
||||
@ -837,7 +835,7 @@ export const Minting = ({
|
||||
</Button>
|
||||
</DialogActions>
|
||||
<Snackbar
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
||||
open={openSnack}
|
||||
autoHideDuration={6000}
|
||||
onClose={handleClose}
|
||||
@ -846,7 +844,7 @@ export const Minting = ({
|
||||
onClose={handleClose}
|
||||
severity={info?.type}
|
||||
variant="filled"
|
||||
sx={{ width: "100%" }}
|
||||
sx={{ width: '100%' }}
|
||||
>
|
||||
{info?.message}
|
||||
</Alert>
|
||||
|
@ -4,56 +4,59 @@ import {
|
||||
TextField,
|
||||
TextFieldProps,
|
||||
styled,
|
||||
} from "@mui/material";
|
||||
import { forwardRef, useState } from "react";
|
||||
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
|
||||
import VisibilityIcon from "@mui/icons-material/Visibility";
|
||||
useTheme,
|
||||
} from '@mui/material';
|
||||
import { forwardRef, useState } from 'react';
|
||||
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
|
||||
import VisibilityIcon from '@mui/icons-material/Visibility';
|
||||
|
||||
export const CustomInput = styled(TextField)(({ theme }) => ({
|
||||
width: "183px",
|
||||
borderRadius: "5px",
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
outline: "none",
|
||||
input: {
|
||||
fontSize: 10,
|
||||
fontFamily: "Inter",
|
||||
fontWeight: 400,
|
||||
color: theme.palette.text.primary,
|
||||
"&::placeholder": {
|
||||
fontSize: 16,
|
||||
color: theme.palette.text.disabled,
|
||||
},
|
||||
outline: "none",
|
||||
padding: "10px",
|
||||
width: '183px',
|
||||
borderRadius: '5px',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
outline: 'none',
|
||||
input: {
|
||||
fontSize: 10,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: 400,
|
||||
color: theme.palette.text.primary,
|
||||
'&::placeholder': {
|
||||
fontSize: 16,
|
||||
color: theme.palette.text.disabled,
|
||||
},
|
||||
"& .MuiOutlinedInput-root": {
|
||||
"& fieldset": {
|
||||
border: `0.5px solid ${theme.palette.divider}`,
|
||||
},
|
||||
"&:hover fieldset": {
|
||||
border: `0.5px solid ${theme.palette.divider}`,
|
||||
},
|
||||
"&.Mui-focused fieldset": {
|
||||
border: `0.5px solid ${theme.palette.divider}`,
|
||||
},
|
||||
outline: 'none',
|
||||
padding: '10px',
|
||||
},
|
||||
'& .MuiOutlinedInput-root': {
|
||||
'& fieldset': {
|
||||
border: `0.5px solid ${theme.palette.divider}`,
|
||||
},
|
||||
"& .MuiInput-underline:before": {
|
||||
borderBottom: "none",
|
||||
'&:hover fieldset': {
|
||||
border: `0.5px solid ${theme.palette.divider}`,
|
||||
},
|
||||
"& .MuiInput-underline:hover:not(.Mui-disabled):before": {
|
||||
borderBottom: "none",
|
||||
'&.Mui-focused fieldset': {
|
||||
border: `0.5px solid ${theme.palette.divider}`,
|
||||
},
|
||||
"& .MuiInput-underline:after": {
|
||||
borderBottom: "none",
|
||||
},
|
||||
}));
|
||||
},
|
||||
'& .MuiInput-underline:before': {
|
||||
borderBottom: 'none',
|
||||
},
|
||||
'& .MuiInput-underline:hover:not(.Mui-disabled):before': {
|
||||
borderBottom: 'none',
|
||||
},
|
||||
'& .MuiInput-underline:after': {
|
||||
borderBottom: 'none',
|
||||
},
|
||||
}));
|
||||
|
||||
export const PasswordField = forwardRef<HTMLInputElement, TextFieldProps>(
|
||||
({ ...props }, ref) => {
|
||||
const [canViewPassword, setCanViewPassword] = useState(false);
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<CustomInput
|
||||
type={canViewPassword ? "text" : "password"}
|
||||
type={canViewPassword ? 'text' : 'password'}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment
|
||||
@ -68,22 +71,14 @@ export const PasswordField = forwardRef<HTMLInputElement, TextFieldProps>(
|
||||
data-testid="plain-text-indicator"
|
||||
sx={{ minWidth: 0, p: 0 }}
|
||||
>
|
||||
<VisibilityOffIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
/>
|
||||
<VisibilityOffIcon />
|
||||
</ButtonBase>
|
||||
) : (
|
||||
<ButtonBase
|
||||
data-testid="password-text-indicator"
|
||||
sx={{ minWidth: 0, p: 0 }}
|
||||
>
|
||||
<VisibilityIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
}}
|
||||
/>
|
||||
<VisibilityIcon />
|
||||
</ButtonBase>
|
||||
)}
|
||||
</InputAdornment>
|
||||
|
@ -1,31 +1,43 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import QMailLogo from '../assets/QMailLogo.png'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { mailsAtom, qMailLastEnteredTimestampAtom } from '../atoms/global'
|
||||
import { isLessThanOneWeekOld } from './Group/QMailMessages'
|
||||
import { ButtonBase, Tooltip } from '@mui/material'
|
||||
import { executeEvent } from '../utils/events'
|
||||
export const QMailStatus = () => {
|
||||
const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState(qMailLastEnteredTimestampAtom)
|
||||
const [mails, setMails] = useRecoilState(mailsAtom)
|
||||
import { useMemo } from 'react';
|
||||
import QMailLogo from '../assets/QMailLogo.png';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { mailsAtom, qMailLastEnteredTimestampAtom } from '../atoms/global';
|
||||
import { isLessThanOneWeekOld } from './Group/QMailMessages';
|
||||
import { ButtonBase, Tooltip } from '@mui/material';
|
||||
import { executeEvent } from '../utils/events';
|
||||
|
||||
const hasNewMail = useMemo(()=> {
|
||||
if(mails?.length === 0) return false
|
||||
const latestMail = mails[0]
|
||||
if(!lastEnteredTimestamp && isLessThanOneWeekOld(latestMail?.created)) return true
|
||||
if((lastEnteredTimestamp < latestMail?.created) && isLessThanOneWeekOld(latestMail?.created)) return true
|
||||
return false
|
||||
}, [lastEnteredTimestamp, mails])
|
||||
export const QMailStatus = () => {
|
||||
const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState(
|
||||
qMailLastEnteredTimestampAtom
|
||||
);
|
||||
const [mails, setMails] = useRecoilState(mailsAtom);
|
||||
|
||||
const hasNewMail = useMemo(() => {
|
||||
if (mails?.length === 0) return false;
|
||||
const latestMail = mails[0];
|
||||
if (!lastEnteredTimestamp && isLessThanOneWeekOld(latestMail?.created))
|
||||
return true;
|
||||
if (
|
||||
lastEnteredTimestamp < latestMail?.created &&
|
||||
isLessThanOneWeekOld(latestMail?.created)
|
||||
)
|
||||
return true;
|
||||
return false;
|
||||
}, [lastEnteredTimestamp, mails]);
|
||||
return (
|
||||
<ButtonBase onClick={()=> {
|
||||
executeEvent("addTab", { data: { service: 'APP', name: 'q-mail' } });
|
||||
executeEvent("open-apps-mode", { });
|
||||
setLastEnteredTimestamp(Date.now())
|
||||
}} style={{
|
||||
position: 'relative'
|
||||
}}>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
executeEvent('addTab', { data: { service: 'APP', name: 'q-mail' } });
|
||||
executeEvent('open-apps-mode', {});
|
||||
setLastEnteredTimestamp(Date.now());
|
||||
}}
|
||||
style={{
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{hasNewMail && (
|
||||
<div style={{
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
zIndex: 1,
|
||||
top: '-7px',
|
||||
@ -34,30 +46,35 @@ export const QMailStatus = () => {
|
||||
height: '15px',
|
||||
width: '15px',
|
||||
borderRadius: '50%',
|
||||
outline: '1px solid white'
|
||||
}} />
|
||||
outline: '1px solid white',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Tooltip
|
||||
title={<span style={{ color: "white", fontSize: "14px", fontWeight: 700 }}>Q-MAIL</span>}
|
||||
title={
|
||||
<span style={{ color: 'white', fontSize: '14px', fontWeight: 700 }}>
|
||||
Q-MAIL
|
||||
</span>
|
||||
}
|
||||
placement="left"
|
||||
arrow
|
||||
sx={{ fontSize: "24" }}
|
||||
sx={{ fontSize: '24' }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: "#ffffff",
|
||||
backgroundColor: "#444444",
|
||||
color: '#ffffff',
|
||||
backgroundColor: '#444444',
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: "#444444",
|
||||
color: '#444444',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<img style={{ width: '24px', height: 'auto' }} src={QMailLogo} />
|
||||
</Tooltip>
|
||||
</ButtonBase>
|
||||
)
|
||||
}
|
||||
</ButtonBase>
|
||||
);
|
||||
};
|
||||
|
@ -1,167 +1,170 @@
|
||||
import { Box, CircularProgress } from '@mui/material';
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { CustomButton, CustomInput, CustomLabel, TextP } from '../App-styles';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
CustomButton,
|
||||
CustomInput,
|
||||
CustomLabel,
|
||||
TextP,
|
||||
} from '../styles/App-styles';
|
||||
import { Spacer } from '../common/Spacer';
|
||||
import BoundedNumericTextField from '../common/BoundedNumericTextField';
|
||||
import { PasswordField } from './PasswordField/PasswordField';
|
||||
import { ErrorText } from './ErrorText/ErrorText';
|
||||
import { getFee } from '../background';
|
||||
|
||||
export const QortPayment = ({balance, show, onSuccess, defaultPaymentTo}) => {
|
||||
const [paymentTo, setPaymentTo] = useState<string>(defaultPaymentTo);
|
||||
const [paymentAmount, setPaymentAmount] = useState<number>(0);
|
||||
const [paymentPassword, setPaymentPassword] = useState<string>("");
|
||||
const [sendPaymentError, setSendPaymentError] = useState<string>("");
|
||||
const [sendPaymentSuccess, setSendPaymentSuccess] = useState<string>("");
|
||||
const [isLoadingSendCoin, setIsLoadingSendCoin] = useState<boolean>(false);
|
||||
export const QortPayment = ({ balance, show, onSuccess, defaultPaymentTo }) => {
|
||||
const [paymentTo, setPaymentTo] = useState<string>(defaultPaymentTo);
|
||||
const [paymentAmount, setPaymentAmount] = useState<number>(0);
|
||||
const [paymentPassword, setPaymentPassword] = useState<string>('');
|
||||
const [sendPaymentError, setSendPaymentError] = useState<string>('');
|
||||
const [sendPaymentSuccess, setSendPaymentSuccess] = useState<string>('');
|
||||
const [isLoadingSendCoin, setIsLoadingSendCoin] = useState<boolean>(false);
|
||||
|
||||
const sendCoinFunc = async () => {
|
||||
try {
|
||||
setSendPaymentError('');
|
||||
setSendPaymentSuccess('');
|
||||
if (!paymentTo) {
|
||||
setSendPaymentError('Please enter a recipient');
|
||||
return;
|
||||
}
|
||||
if (!paymentAmount) {
|
||||
setSendPaymentError('Please enter an amount greater than 0');
|
||||
return;
|
||||
}
|
||||
if (!paymentPassword) {
|
||||
setSendPaymentError('Please enter your wallet password');
|
||||
return;
|
||||
}
|
||||
const fee = await getFee('PAYMENT');
|
||||
|
||||
|
||||
const sendCoinFunc = async() => {
|
||||
try {
|
||||
setSendPaymentError("");
|
||||
setSendPaymentSuccess("");
|
||||
if (!paymentTo) {
|
||||
setSendPaymentError("Please enter a recipient");
|
||||
return;
|
||||
await show({
|
||||
message: `Would you like to transfer ${Number(paymentAmount)} QORT?`,
|
||||
paymentFee: fee.fee + ' QORT',
|
||||
});
|
||||
setIsLoadingSendCoin(true);
|
||||
window
|
||||
.sendMessage('sendCoin', {
|
||||
amount: Number(paymentAmount),
|
||||
receiver: paymentTo.trim(),
|
||||
password: paymentPassword,
|
||||
})
|
||||
.then((response) => {
|
||||
if (response?.error) {
|
||||
setSendPaymentError(response.error);
|
||||
} else {
|
||||
onSuccess();
|
||||
}
|
||||
if (!paymentAmount) {
|
||||
setSendPaymentError("Please enter an amount greater than 0");
|
||||
return;
|
||||
}
|
||||
if (!paymentPassword) {
|
||||
setSendPaymentError("Please enter your wallet password");
|
||||
return;
|
||||
}
|
||||
const fee = await getFee('PAYMENT')
|
||||
|
||||
await show({
|
||||
message: `Would you like to transfer ${Number(paymentAmount)} QORT?` ,
|
||||
paymentFee: fee.fee + ' QORT'
|
||||
})
|
||||
setIsLoadingSendCoin(true);
|
||||
window
|
||||
.sendMessage("sendCoin", {
|
||||
amount: Number(paymentAmount),
|
||||
receiver: paymentTo.trim(),
|
||||
password: paymentPassword,
|
||||
})
|
||||
.then((response) => {
|
||||
if (response?.error) {
|
||||
setSendPaymentError(response.error);
|
||||
} else {
|
||||
onSuccess()
|
||||
|
||||
}
|
||||
setIsLoadingSendCoin(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed to send coin:", error);
|
||||
setIsLoadingSendCoin(false);
|
||||
});
|
||||
} catch (error) {
|
||||
// error
|
||||
}
|
||||
};
|
||||
setIsLoadingSendCoin(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to send coin:', error);
|
||||
setIsLoadingSendCoin(false);
|
||||
});
|
||||
} catch (error) {
|
||||
// error
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<TextP
|
||||
sx={{
|
||||
textAlign: "start",
|
||||
lineHeight: "24px",
|
||||
fontSize: "20px",
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
Transfer QORT
|
||||
</TextP>
|
||||
<Spacer height="35px" />
|
||||
<TextP
|
||||
sx={{
|
||||
textAlign: "start",
|
||||
lineHeight: "16px",
|
||||
fontSize: "20px",
|
||||
fontWeight: 600,
|
||||
color: "rgba(255, 255, 255, 0.5)",
|
||||
}}
|
||||
>
|
||||
Balance:
|
||||
</TextP>
|
||||
<TextP
|
||||
sx={{
|
||||
textAlign: "start",
|
||||
lineHeight: "24px",
|
||||
fontSize: "20px",
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
{balance?.toFixed(2)} QORT
|
||||
</TextP>
|
||||
</Box>
|
||||
<Spacer height="35px" />
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<TextP
|
||||
sx={{
|
||||
textAlign: 'start',
|
||||
lineHeight: '24px',
|
||||
fontSize: '20px',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
Transfer QORT
|
||||
</TextP>
|
||||
<Spacer height="35px" />
|
||||
<TextP
|
||||
sx={{
|
||||
textAlign: 'start',
|
||||
lineHeight: '16px',
|
||||
fontSize: '20px',
|
||||
fontWeight: 600,
|
||||
color: 'rgba(255, 255, 255, 0.5)',
|
||||
}}
|
||||
>
|
||||
Balance:
|
||||
</TextP>
|
||||
<TextP
|
||||
sx={{
|
||||
textAlign: 'start',
|
||||
lineHeight: '24px',
|
||||
fontSize: '20px',
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
{balance?.toFixed(2)} QORT
|
||||
</TextP>
|
||||
</Box>
|
||||
<Spacer height="35px" />
|
||||
|
||||
<Box>
|
||||
<CustomLabel htmlFor="standard-adornment-name">To</CustomLabel>
|
||||
<Spacer height="5px" />
|
||||
<CustomInput
|
||||
id="standard-adornment-name"
|
||||
value={paymentTo}
|
||||
onChange={(e) => setPaymentTo(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<Spacer height="6px" />
|
||||
<CustomLabel htmlFor="standard-adornment-amount">
|
||||
Amount
|
||||
</CustomLabel>
|
||||
<Spacer height="5px" />
|
||||
<BoundedNumericTextField
|
||||
value={paymentAmount}
|
||||
minValue={0}
|
||||
maxValue={+balance}
|
||||
allowDecimals={true}
|
||||
initialValue={'0'}
|
||||
allowNegatives={false}
|
||||
afterChange={(e: string) => setPaymentAmount(+e)}
|
||||
/>
|
||||
<Spacer height="6px" />
|
||||
<CustomLabel htmlFor="standard-adornment-password">
|
||||
Confirm Wallet Password
|
||||
</CustomLabel>
|
||||
<Spacer height="5px" />
|
||||
<PasswordField
|
||||
id="standard-adornment-password"
|
||||
value={paymentPassword}
|
||||
onChange={(e) => setPaymentPassword(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</Box>
|
||||
<Spacer height="10px" />
|
||||
<ErrorText>{sendPaymentError}</ErrorText>
|
||||
{/* <Typography>{sendPaymentSuccess}</Typography> */}
|
||||
<Spacer height="25px" />
|
||||
<CustomButton
|
||||
<Box>
|
||||
<CustomLabel htmlFor="standard-adornment-name">To</CustomLabel>
|
||||
<Spacer height="5px" />
|
||||
<CustomInput
|
||||
id="standard-adornment-name"
|
||||
value={paymentTo}
|
||||
onChange={(e) => setPaymentTo(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<Spacer height="6px" />
|
||||
<CustomLabel htmlFor="standard-adornment-amount">Amount</CustomLabel>
|
||||
<Spacer height="5px" />
|
||||
<BoundedNumericTextField
|
||||
value={paymentAmount}
|
||||
minValue={0}
|
||||
maxValue={+balance}
|
||||
allowDecimals={true}
|
||||
initialValue={'0'}
|
||||
allowNegatives={false}
|
||||
afterChange={(e: string) => setPaymentAmount(+e)}
|
||||
/>
|
||||
<Spacer height="6px" />
|
||||
<CustomLabel htmlFor="standard-adornment-password">
|
||||
Confirm Wallet Password
|
||||
</CustomLabel>
|
||||
<Spacer height="5px" />
|
||||
<PasswordField
|
||||
id="standard-adornment-password"
|
||||
value={paymentPassword}
|
||||
onChange={(e) => setPaymentPassword(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</Box>
|
||||
<Spacer height="10px" />
|
||||
<ErrorText>{sendPaymentError}</ErrorText>
|
||||
{/* <Typography>{sendPaymentSuccess}</Typography> */}
|
||||
<Spacer height="25px" />
|
||||
<CustomButton
|
||||
sx={{
|
||||
cursor: isLoadingSendCoin ? 'default' : 'pointer',
|
||||
}}
|
||||
onClick={() => {
|
||||
if (isLoadingSendCoin) return;
|
||||
sendCoinFunc();
|
||||
}}
|
||||
>
|
||||
{isLoadingSendCoin && (
|
||||
<CircularProgress
|
||||
size={16}
|
||||
sx={{
|
||||
cursor: isLoadingSendCoin ? 'default' : 'pointer'
|
||||
color: 'white',
|
||||
}}
|
||||
onClick={() => {
|
||||
if(isLoadingSendCoin) return
|
||||
sendCoinFunc();
|
||||
}}
|
||||
>
|
||||
{isLoadingSendCoin && (
|
||||
<CircularProgress size={16} sx={{
|
||||
color: 'white'
|
||||
}} />
|
||||
)}
|
||||
Send
|
||||
</CustomButton>
|
||||
/>
|
||||
)}
|
||||
Send
|
||||
</CustomButton>
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -1,24 +1,19 @@
|
||||
import { createContext, useContext, useState, useMemo } from 'react';
|
||||
import { createTheme, ThemeProvider as MuiThemeProvider } from '@mui/material/styles';
|
||||
import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles';
|
||||
import { darkTheme, lightTheme } from '../../styles/theme';
|
||||
|
||||
const darkTheme = createTheme({
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
},
|
||||
const ThemeContext = createContext({
|
||||
themeMode: 'light',
|
||||
toggleTheme: () => {},
|
||||
});
|
||||
|
||||
const lightTheme = createTheme({
|
||||
palette: {
|
||||
mode: 'light',
|
||||
},
|
||||
});
|
||||
|
||||
const ThemeContext = createContext({ themeMode: 'light', toggleTheme: () => {} });
|
||||
|
||||
export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [themeMode, setThemeMode] = useState('light');
|
||||
|
||||
const theme = useMemo(() => (themeMode === 'light' ? lightTheme : darkTheme), [themeMode]);
|
||||
const theme = useMemo(
|
||||
() => (themeMode === 'light' ? lightTheme : darkTheme),
|
||||
[themeMode]
|
||||
);
|
||||
|
||||
const toggleTheme = () => {
|
||||
setThemeMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light'));
|
||||
@ -31,4 +26,4 @@ export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const useThemeContext = () => useContext(ThemeContext);
|
||||
export const useThemeContext = () => useContext(ThemeContext);
|
||||
|
@ -1,58 +1,58 @@
|
||||
import { useThemeContext } from "./ThemeContext";
|
||||
import { styled, Switch } from "@mui/material";
|
||||
import { useThemeContext } from './ThemeContext';
|
||||
import { styled, Switch } from '@mui/material';
|
||||
|
||||
const ThemeSwitch = styled(Switch)(({ theme }) => ({
|
||||
width: 62,
|
||||
height: 34,
|
||||
padding: 7,
|
||||
"& .MuiSwitch-switchBase": {
|
||||
'& .MuiSwitch-switchBase': {
|
||||
margin: 1,
|
||||
padding: 0,
|
||||
transform: "translateX(6px)",
|
||||
"&.Mui-checked": {
|
||||
color: "#fff",
|
||||
transform: "translateX(22px)",
|
||||
"& .MuiSwitch-thumb:before": {
|
||||
transform: 'translateX(6px)',
|
||||
'&.Mui-checked': {
|
||||
color: '#fff',
|
||||
transform: 'translateX(22px)',
|
||||
'& .MuiSwitch-thumb:before': {
|
||||
backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 20 20"><path fill="${encodeURIComponent(
|
||||
"#fff"
|
||||
'#fff'
|
||||
)}" d="M4.2 2.5l-.7 1.8-1.8.7 1.8.7.7 1.8.6-1.8L6.7 5l-1.9-.7-.6-1.8zm15 8.3a6.7 6.7 0 11-6.6-6.6 5.8 5.8 0 006.6 6.6z"/></svg>')`,
|
||||
},
|
||||
"& + .MuiSwitch-track": {
|
||||
'& + .MuiSwitch-track': {
|
||||
opacity: 1,
|
||||
backgroundColor: "#aab4be",
|
||||
...theme.applyStyles("dark", {
|
||||
backgroundColor: "#8796A5",
|
||||
backgroundColor: '#aab4be',
|
||||
...theme.applyStyles('dark', {
|
||||
backgroundColor: '#8796A5',
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
"& .MuiSwitch-thumb": {
|
||||
backgroundColor: "#001e3c",
|
||||
'& .MuiSwitch-thumb': {
|
||||
backgroundColor: '#fde402',
|
||||
width: 32,
|
||||
height: 32,
|
||||
"&::before": {
|
||||
'&::before': {
|
||||
content: "''",
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
left: 0,
|
||||
top: 0,
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundPosition: 'center',
|
||||
backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 20 20"><path fill="${encodeURIComponent(
|
||||
"#fff"
|
||||
'#000000'
|
||||
)}" d="M9.305 1.667V3.75h1.389V1.667h-1.39zm-4.707 1.95l-.982.982L5.09 6.072l.982-.982-1.473-1.473zm10.802 0L13.927 5.09l.982.982 1.473-1.473-.982-.982zM10 5.139a4.872 4.872 0 00-4.862 4.86A4.872 4.872 0 0010 14.862 4.872 4.872 0 0014.86 10 4.872 4.872 0 0010 5.139zm0 1.389A3.462 3.462 0 0113.471 10a3.462 3.462 0 01-3.473 3.472A3.462 3.462 0 016.527 10 3.462 3.462 0 0110 6.528zM1.665 9.305v1.39h2.083v-1.39H1.666zm14.583 0v1.39h2.084v-1.39h-2.084zM5.09 13.928L3.616 15.4l.982.982 1.473-1.473-.982-.982zm9.82 0l-.982.982 1.473 1.473.982-.982-1.473-1.473zM9.305 16.25v2.083h1.389V16.25h-1.39z"/></svg>')`,
|
||||
},
|
||||
...theme.applyStyles("dark", {
|
||||
backgroundColor: "#003892",
|
||||
...theme.applyStyles('dark', {
|
||||
backgroundColor: '#003892',
|
||||
}),
|
||||
},
|
||||
"& .MuiSwitch-track": {
|
||||
'& .MuiSwitch-track': {
|
||||
opacity: 1,
|
||||
backgroundColor: "#aab4be",
|
||||
backgroundColor: '#aab4be',
|
||||
borderRadius: 20 / 2,
|
||||
...theme.applyStyles("dark", {
|
||||
backgroundColor: "#8796A5",
|
||||
...theme.applyStyles('dark', {
|
||||
backgroundColor: '#8796A5',
|
||||
}),
|
||||
},
|
||||
}));
|
||||
@ -62,14 +62,14 @@ const ThemeSelector = ({ style }) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "1px",
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '1px',
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
<ThemeSwitch checked={themeMode === "dark"} onChange={toggleTheme} />
|
||||
<ThemeSwitch checked={themeMode === 'dark'} onChange={toggleTheme} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -169,12 +169,15 @@ useEffect(()=> {
|
||||
onClick={async () => {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
if(isAlreadyBlocked === true){
|
||||
await removeBlockFromList(address, name)
|
||||
} else if(isAlreadyBlocked === false) {
|
||||
await addToBlockList(address, name)
|
||||
}
|
||||
executeEvent('updateChatMessagesWithBlocks', true)
|
||||
executeEvent("blockUserFromOutside", {
|
||||
user: address
|
||||
})
|
||||
// if(isAlreadyBlocked === true){
|
||||
// await removeBlockFromList(address, name)
|
||||
// } else if(isAlreadyBlocked === false) {
|
||||
// await addToBlockList(address, name)
|
||||
// }
|
||||
// executeEvent('updateChatMessagesWithBlocks', true)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
|
17
src/main.tsx
17
src/main.tsx
@ -1,13 +1,12 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App.tsx";
|
||||
import "./index.css";
|
||||
import "./messaging/messagesToBackground";
|
||||
import { MessageQueueProvider } from "./MessageQueueContext.tsx";
|
||||
import { RecoilRoot } from "recoil";
|
||||
import { ThemeProvider } from "./components/Theme/ThemeContext.tsx";
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
import '../src/styles/index.css';
|
||||
import './messaging/messagesToBackground';
|
||||
import { MessageQueueProvider } from './MessageQueueContext.tsx';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { ThemeProvider } from './components/Theme/ThemeContext.tsx';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<>
|
||||
<ThemeProvider>
|
||||
<MessageQueueProvider>
|
||||
|
260
src/styles/App-styles.ts
Normal file
260
src/styles/App-styles.ts
Normal file
@ -0,0 +1,260 @@
|
||||
import { Typography, Box, TextField, InputLabel } from '@mui/material';
|
||||
import { styled } from '@mui/system';
|
||||
|
||||
export const AppContainer = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
radius: '15px',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
overflow: 'hidden',
|
||||
}));
|
||||
|
||||
export const AuthenticatedContainer = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AuthenticatedContainerInnerLeft = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AuthenticatedContainerInnerRight = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
width: '60px',
|
||||
height: '100%',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AuthenticatedContainerInnerTop = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
width: '100%px',
|
||||
height: '60px',
|
||||
padding: '20px',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const TextP = styled(Typography)(({ theme }) => ({
|
||||
fontSize: '13px',
|
||||
fontWeight: 600,
|
||||
fontFamily: 'Inter',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const TextItalic = styled('span')(({ theme }) => ({
|
||||
fontSize: '13px',
|
||||
fontWeight: 600,
|
||||
fontFamily: 'Inter',
|
||||
fontStyle: 'italic',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const TextSpan = styled('span')(({ theme }) => ({
|
||||
fontSize: '13px',
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: 800,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
export const AddressBox = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
border: `1px solid ${
|
||||
theme.palette.mode === 'dark'
|
||||
? 'rgba(255, 255, 255, 0.5)'
|
||||
: 'rgba(0, 0, 0, 0.3)'
|
||||
}`,
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
width: 'auto',
|
||||
height: '25px',
|
||||
padding: '5px 15px',
|
||||
gap: '5px',
|
||||
borderRadius: '100px',
|
||||
fontFamily: 'Inter',
|
||||
fontSize: '12px',
|
||||
fontWeight: 600,
|
||||
lineHeight: '14.52px',
|
||||
textAlign: 'left',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s',
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor:
|
||||
theme.palette.mode === 'dark'
|
||||
? 'rgba(41, 41, 43, 1)'
|
||||
: 'rgba(240, 240, 240, 1)',
|
||||
color: theme.palette.mode === 'dark' ? '#fff' : '#000',
|
||||
|
||||
'svg path': {
|
||||
fill: theme.palette.mode === 'dark' ? '#fff' : '#000',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export const CustomButton = styled(Box)(({ theme }) => ({
|
||||
boxSizing: 'border-box',
|
||||
padding: '15px 20px',
|
||||
gap: '10px',
|
||||
|
||||
border: `0.5px solid ${
|
||||
theme.palette.mode === 'dark'
|
||||
? 'rgba(255, 255, 255, 0.5)'
|
||||
: 'rgba(0, 0, 0, 0.3)'
|
||||
}`,
|
||||
filter: 'drop-shadow(1px 4px 10.5px rgba(0, 0, 0, 0.3))',
|
||||
borderRadius: '5px',
|
||||
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
|
||||
width: 'fit-content',
|
||||
minWidth: '160px',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s',
|
||||
|
||||
fontWeight: 600,
|
||||
fontFamily: 'Inter',
|
||||
textAlign: 'center',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor:
|
||||
theme.palette.mode === 'dark'
|
||||
? 'rgba(41, 41, 43, 1)'
|
||||
: 'rgba(230, 230, 230, 1)',
|
||||
color: '#fff',
|
||||
|
||||
'svg path': {
|
||||
fill: '#fff',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
interface CustomButtonProps {
|
||||
bgColor?: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export const CustomButtonAccept = styled(Box)<CustomButtonProps>(
|
||||
({ bgColor, color, theme }) => ({
|
||||
boxSizing: 'border-box',
|
||||
padding: '15px 20px',
|
||||
gap: '10px',
|
||||
border: `0.5px solid ${
|
||||
theme.palette.mode === 'dark'
|
||||
? 'rgba(255, 255, 255, 0.5)'
|
||||
: 'rgba(0, 0, 0, 0.3)'
|
||||
}`,
|
||||
filter: 'drop-shadow(1px 4px 10.5px rgba(0,0,0,0.3))',
|
||||
borderRadius: 5,
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: 'fit-content',
|
||||
transition: 'all 0.2s',
|
||||
minWidth: 160,
|
||||
cursor: 'pointer',
|
||||
fontWeight: 600,
|
||||
fontFamily: 'Inter',
|
||||
textAlign: 'center',
|
||||
opacity: 0.7,
|
||||
|
||||
// Color and backgroundColor with fallbacks
|
||||
backgroundColor:
|
||||
bgColor || (theme.palette.mode === 'dark' ? '#1d1d1d' : '#f5f5f5'),
|
||||
color: color || (theme.palette.mode === 'dark' ? '#fff' : '#000'),
|
||||
|
||||
'&:hover': {
|
||||
opacity: 1,
|
||||
backgroundColor:
|
||||
bgColor ||
|
||||
(theme.palette.mode === 'dark'
|
||||
? 'rgba(41, 41, 43, 1)'
|
||||
: 'rgba(230, 230, 230, 1)'),
|
||||
color: color || '#fff',
|
||||
svg: {
|
||||
path: {
|
||||
fill: color || '#fff',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
export const CustomInput = styled(TextField)(({ theme }) => ({
|
||||
width: '183px', // Adjust the width as needed
|
||||
borderRadius: '5px',
|
||||
// backgroundColor: "rgba(30, 30, 32, 1)",
|
||||
outline: 'none',
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: theme.palette.text.primary,
|
||||
input: {
|
||||
fontSize: 10,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: 400,
|
||||
color: 'white',
|
||||
'&::placeholder': {
|
||||
fontSize: 16,
|
||||
color: 'rgba(255, 255, 255, 0.2)',
|
||||
},
|
||||
outline: 'none',
|
||||
padding: '10px',
|
||||
},
|
||||
'& .MuiOutlinedInput-root': {
|
||||
'& fieldset': {
|
||||
border: '0.5px solid rgba(255, 255, 255, 0.5)',
|
||||
},
|
||||
'&:hover fieldset': {
|
||||
border: '0.5px solid rgba(255, 255, 255, 0.5)',
|
||||
},
|
||||
'&.Mui-focused fieldset': {
|
||||
border: '0.5px solid rgba(255, 255, 255, 0.5)',
|
||||
},
|
||||
},
|
||||
'& .MuiInput-underline:before': {
|
||||
borderBottom: 'none',
|
||||
},
|
||||
'& .MuiInput-underline:hover:not(.Mui-disabled):before': {
|
||||
borderBottom: 'none',
|
||||
},
|
||||
'& .MuiInput-underline:after': {
|
||||
borderBottom: 'none',
|
||||
},
|
||||
}));
|
||||
|
||||
export const CustomLabel = styled(InputLabel)(({ theme }) => ({
|
||||
fontWeight: 400,
|
||||
fontFamily: 'Inter',
|
||||
fontSize: '10px',
|
||||
lineHeight: '12px',
|
||||
color:
|
||||
theme.palette.mode === 'dark'
|
||||
? 'rgba(255, 255, 255, 0.5)'
|
||||
: 'rgba(0, 0, 0, 0.5)',
|
||||
}));
|
@ -9,25 +9,32 @@
|
||||
}
|
||||
|
||||
.tooltip .bottom {
|
||||
min-width: 225px;
|
||||
max-width: 250px;
|
||||
top: 35px;
|
||||
right: 0px;
|
||||
/* transform: translate(-50%, 0); */
|
||||
padding: 10px 10px;
|
||||
color: var(--black);
|
||||
background-color: var(--bg-2);
|
||||
font-weight: normal;
|
||||
font-size: 13px;
|
||||
border-radius: 8px;
|
||||
position: absolute;
|
||||
z-index: 99999999;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.5);
|
||||
border: 1px solid var(--black);
|
||||
visibility: hidden;
|
||||
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.5);
|
||||
box-sizing: border-box;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
max-width: 250px;
|
||||
min-width: 225px;
|
||||
opacity: 0;
|
||||
padding: 10px 10px;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 35px;
|
||||
transition: opacity 0.2s;
|
||||
visibility: hidden;
|
||||
z-index: 99999999;
|
||||
}
|
||||
|
||||
.tooltip[data-theme='light'] .bottom {
|
||||
background-color: #f1f1f1;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.tooltip[data-theme='dark'] .bottom {
|
||||
background-color: var(--bg-2);
|
||||
color: var(--black);
|
||||
}
|
||||
|
||||
.tooltip:hover .bottom {
|
||||
@ -47,13 +54,13 @@
|
||||
}
|
||||
|
||||
.tooltip .bottom i::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 50%) rotate(45deg);
|
||||
background-color: var(--white);
|
||||
border: 1px solid var(--black);
|
||||
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.5);
|
||||
content: '';
|
||||
height: 12px;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
transform: translate(-50%, 50%) rotate(45deg);
|
||||
width: 12px;
|
||||
}
|
@ -1,21 +1,21 @@
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
src: url("./styles/fonts/Inter-SemiBold.ttf") format("truetype");
|
||||
font-family: 'Inter';
|
||||
src: url('./styles/fonts/Inter-SemiBold.ttf') format('truetype');
|
||||
font-weight: 600;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
src: url("./styles/fonts/Inter-ExtraBold.ttf") format("truetype");
|
||||
font-family: 'Inter';
|
||||
src: url('./styles/fonts/Inter-ExtraBold.ttf') format('truetype');
|
||||
font-weight: 800;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
src: url("./styles/fonts/Inter-Bold.ttf") format("truetype");
|
||||
font-family: 'Inter';
|
||||
src: url('./styles/fonts/Inter-Bold.ttf') format('truetype');
|
||||
font-weight: 700;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
src: url("./styles/fonts/Inter-Regular.ttf") format("truetype");
|
||||
font-family: 'Inter';
|
||||
src: url('./styles/fonts/Inter-Regular.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ body {
|
||||
}
|
||||
|
||||
@property --var1 {
|
||||
syntax: "<color>";
|
||||
syntax: '<color>';
|
||||
inherits: true;
|
||||
initial-value: transparent;
|
||||
}
|
@ -1,54 +1,50 @@
|
||||
import { createTheme } from '@mui/material/styles'
|
||||
|
||||
import { createTheme } from '@mui/material/styles';
|
||||
|
||||
// Extend the Theme interface
|
||||
|
||||
const commonThemeOptions = {
|
||||
typography: {
|
||||
fontFamily: [
|
||||
'Roboto'
|
||||
].join(','),
|
||||
fontFamily: ['Roboto'].join(','),
|
||||
h1: {
|
||||
fontSize: '2rem',
|
||||
fontWeight: 600
|
||||
fontWeight: 600,
|
||||
},
|
||||
h2: {
|
||||
fontSize: '1.75rem',
|
||||
fontWeight: 500
|
||||
fontWeight: 500,
|
||||
},
|
||||
h3: {
|
||||
fontSize: '1.5rem',
|
||||
fontWeight: 500
|
||||
fontWeight: 500,
|
||||
},
|
||||
h4: {
|
||||
fontSize: '1.25rem',
|
||||
fontWeight: 500
|
||||
fontWeight: 500,
|
||||
},
|
||||
h5: {
|
||||
fontSize: '1rem',
|
||||
fontWeight: 500
|
||||
fontWeight: 500,
|
||||
},
|
||||
h6: {
|
||||
fontSize: '0.875rem',
|
||||
fontWeight: 500
|
||||
fontWeight: 500,
|
||||
},
|
||||
body1: {
|
||||
fontSize: '23px',
|
||||
fontWeight: 400,
|
||||
lineHeight: 1.5,
|
||||
letterSpacing: '0.5px'
|
||||
letterSpacing: 'normal',
|
||||
},
|
||||
|
||||
body2: {
|
||||
fontSize: '18px',
|
||||
fontWeight: 400,
|
||||
lineHeight: 1.4,
|
||||
letterSpacing: '0.2px'
|
||||
}
|
||||
letterSpacing: '0.2px',
|
||||
},
|
||||
},
|
||||
spacing: 8,
|
||||
shape: {
|
||||
borderRadius: 4
|
||||
borderRadius: 4,
|
||||
},
|
||||
breakpoints: {
|
||||
values: {
|
||||
@ -56,8 +52,8 @@ const commonThemeOptions = {
|
||||
sm: 600,
|
||||
md: 900,
|
||||
lg: 1200,
|
||||
xl: 1536
|
||||
}
|
||||
xl: 1536,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
MuiButton: {
|
||||
@ -66,25 +62,24 @@ const commonThemeOptions = {
|
||||
backgroundColor: 'inherit',
|
||||
transition: 'filter 0.3s ease-in-out',
|
||||
'&:hover': {
|
||||
filter: 'brightness(1.1)'
|
||||
}
|
||||
}
|
||||
filter: 'brightness(1.1)',
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultProps: {
|
||||
disableElevation: true,
|
||||
disableRipple: true
|
||||
}
|
||||
disableRipple: true,
|
||||
},
|
||||
},
|
||||
MuiModal: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
zIndex: 50000,
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const lightTheme = createTheme({
|
||||
...commonThemeOptions,
|
||||
@ -93,46 +88,46 @@ const lightTheme = createTheme({
|
||||
primary: {
|
||||
main: '#f4f4fb',
|
||||
dark: '#eaecf4',
|
||||
light: '#f9f9fd'
|
||||
light: '#f9f9fd',
|
||||
},
|
||||
secondary: {
|
||||
main: '#1EAAF1'
|
||||
main: '#c2deec',
|
||||
},
|
||||
background: {
|
||||
default: '#fafafa',
|
||||
paper: '#f0f0f0'
|
||||
paper: '#f0f0f0',
|
||||
},
|
||||
text: {
|
||||
primary: '#000000',
|
||||
secondary: '#525252'
|
||||
}
|
||||
secondary: '#525252',
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
MuiCard: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
boxShadow:
|
||||
'rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px;',
|
||||
'rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(230, 200, 200, 0.06) 0px 1px 2px 0px;',
|
||||
borderRadius: '8px',
|
||||
transition: 'all 0.3s ease-in-out',
|
||||
'&:hover': {
|
||||
cursor: 'pointer',
|
||||
boxShadow:
|
||||
'rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;'
|
||||
}
|
||||
}
|
||||
}
|
||||
'rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiIcon: {
|
||||
defaultProps: {
|
||||
style: {
|
||||
color: '#000000'
|
||||
}
|
||||
}
|
||||
}
|
||||
color: '#000000',
|
||||
opacity: 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
const darkTheme = createTheme({
|
||||
...commonThemeOptions,
|
||||
@ -140,45 +135,46 @@ const darkTheme = createTheme({
|
||||
mode: 'dark',
|
||||
primary: {
|
||||
main: '#2e3d60',
|
||||
dark: "#1a2744",
|
||||
light: "#3f4b66",
|
||||
dark: '#1a2744',
|
||||
light: '#3f4b66',
|
||||
},
|
||||
secondary: {
|
||||
main: '#45adff'
|
||||
main: '#45adff',
|
||||
},
|
||||
|
||||
background: {
|
||||
default: '#313338',
|
||||
paper: "#1e1e20"
|
||||
paper: '#1e1e20',
|
||||
},
|
||||
text: {
|
||||
primary: '#ffffff',
|
||||
secondary: '#b3b3b3'
|
||||
}
|
||||
secondary: '#b3b3b3',
|
||||
},
|
||||
},
|
||||
components: {
|
||||
MuiCard: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
boxShadow: "none",
|
||||
boxShadow: 'none',
|
||||
borderRadius: '8px',
|
||||
transition: 'all 0.3s ease-in-out',
|
||||
'&:hover': {
|
||||
cursor: 'pointer',
|
||||
boxShadow:
|
||||
' 0px 3px 4px 0px hsla(0,0%,0%,0.14), 0px 3px 3px -2px hsla(0,0%,0%,0.12), 0px 1px 8px 0px hsla(0,0%,0%,0.2);'
|
||||
}
|
||||
}
|
||||
}
|
||||
' 0px 3px 4px 0px hsla(0,0%,0%,0.14), 0px 3px 3px -2px hsla(0,0%,0%,0.12), 0px 1px 8px 0px hsla(0,0%,0%,0.2);',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiIcon: {
|
||||
defaultProps: {
|
||||
style: {
|
||||
color: '#ffffff'
|
||||
}
|
||||
}
|
||||
}
|
||||
color: '#ffffff',
|
||||
opacity: 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
export { lightTheme, darkTheme }
|
||||
export { lightTheme, darkTheme };
|
||||
|
Loading…
x
Reference in New Issue
Block a user