Format code, rmove unused

This commit is contained in:
Nicola Benaglia 2025-04-12 16:40:32 +02:00
parent 4f9b8fe6cc
commit 2a41667ef8
16 changed files with 3969 additions and 3295 deletions

View File

@ -6,7 +6,7 @@ import React, {
useState, useState,
} from 'react'; } from 'react';
import { Spacer } from '../common/Spacer'; import { Spacer } from '../common/Spacer';
import { CustomButton, TextP, TextSpan } from '../App-styles'; import { CustomButton, TextP, TextSpan } from '../styles/App-styles';
import { import {
Box, Box,
Button, Button,
@ -29,7 +29,7 @@ import { CustomizedSnackbars } from '../components/Snackbar/Snackbar';
import { cleanUrl, gateways } from '../background'; import { cleanUrl, gateways } from '../background';
import { GlobalContext } from '../App'; import { GlobalContext } from '../App';
import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip'; import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip';
import ThemeSelector from '../styles/ThemeSelector'; import ThemeSelector from '../components/Theme/ThemeSelector';
const manifestData = { const manifestData = {
version: '0.5.3', version: '0.5.3',

View File

@ -3,15 +3,15 @@ import {
InputAdornment, InputAdornment,
TextField, TextField,
TextFieldProps, TextFieldProps,
} from "@mui/material"; } from '@mui/material';
import React, { useRef, useState } from "react"; import React, { useRef, useState } from 'react';
import AddIcon from "@mui/icons-material/Add"; import AddIcon from '@mui/icons-material/Add';
import RemoveIcon from "@mui/icons-material/Remove"; import RemoveIcon from '@mui/icons-material/Remove';
import { import {
removeTrailingZeros, removeTrailingZeros,
setNumberWithinBounds, setNumberWithinBounds,
} from "./numberFunctions.ts"; } from './numberFunctions.ts';
import { CustomInput } from "../App-styles.ts"; import { CustomInput } from '../styles/App-styles.ts';
type eventType = React.ChangeEvent<HTMLInputElement>; type eventType = React.ChangeEvent<HTMLInputElement>;
type BoundedNumericTextFieldProps = { type BoundedNumericTextFieldProps = {
@ -37,18 +37,18 @@ export const BoundedNumericTextField = ({
...props ...props
}: BoundedNumericTextFieldProps) => { }: BoundedNumericTextFieldProps) => {
const [textFieldValue, setTextFieldValue] = useState<string>( const [textFieldValue, setTextFieldValue] = useState<string>(
initialValue || "" initialValue || ''
); );
const ref = useRef<HTMLInputElement | null>(null); const ref = useRef<HTMLInputElement | null>(null);
const stringIsEmpty = (value: string) => { const stringIsEmpty = (value: string) => {
return value === ""; return value === '';
}; };
const isAllZerosNum = /^0*\.?0*$/; const isAllZerosNum = /^0*\.?0*$/;
const isFloatNum = /^-?[0-9]*\.?[0-9]*$/; const isFloatNum = /^-?[0-9]*\.?[0-9]*$/;
const isIntegerNum = /^-?[0-9]+$/; const isIntegerNum = /^-?[0-9]+$/;
const skipMinMaxCheck = (value: string) => { const skipMinMaxCheck = (value: string) => {
const lastIndexIsDecimal = value.charAt(value.length - 1) === "."; const lastIndexIsDecimal = value.charAt(value.length - 1) === '.';
const isEmpty = stringIsEmpty(value); const isEmpty = stringIsEmpty(value);
const isAllZeros = isAllZerosNum.test(value); const isAllZeros = isAllZerosNum.test(value);
const isInteger = isIntegerNum.test(value); const isInteger = isIntegerNum.test(value);
@ -69,7 +69,7 @@ export const BoundedNumericTextField = ({
const getSigDigits = (number: string) => { const getSigDigits = (number: string) => {
if (isIntegerNum.test(number)) return 0; if (isIntegerNum.test(number)) return 0;
const decimalSplit = number.split("."); const decimalSplit = number.split('.');
return decimalSplit[decimalSplit.length - 1].length; return decimalSplit[decimalSplit.length - 1].length;
}; };
@ -78,15 +78,15 @@ export const BoundedNumericTextField = ({
}; };
const filterTypes = (value: string) => { const filterTypes = (value: string) => {
if (allowDecimals === false) value = value.replace(".", ""); if (allowDecimals === false) value = value.replace('.', '');
if (allowNegatives === false) value = value.replace("-", ""); if (allowNegatives === false) value = value.replace('-', '');
if (sigDigitsExceeded(value, maxSigDigits)) { if (sigDigitsExceeded(value, maxSigDigits)) {
value = value.substring(0, value.length - 1); value = value.substring(0, value.length - 1);
} }
return value; return value;
}; };
const filterValue = (value: string) => { const filterValue = (value: string) => {
if (stringIsEmpty(value)) return ""; if (stringIsEmpty(value)) return '';
value = filterTypes(value); value = filterTypes(value);
if (isFloatNum.test(value)) { if (isFloatNum.test(value)) {
return setMinMaxValue(value); return setMinMaxValue(value);
@ -109,8 +109,8 @@ export const BoundedNumericTextField = ({
const formatValueOnBlur = (e: eventType) => { const formatValueOnBlur = (e: eventType) => {
let value = e.target.value; let value = e.target.value;
if (stringIsEmpty(value) || value === ".") { if (stringIsEmpty(value) || value === '.') {
setTextFieldValue(""); setTextFieldValue('');
return; return;
} }
@ -129,23 +129,33 @@ export const BoundedNumericTextField = ({
...props?.InputProps, ...props?.InputProps,
endAdornment: addIconButtons ? ( endAdornment: addIconButtons ? (
<InputAdornment position="end"> <InputAdornment position="end">
<IconButton size="small" onClick={() => changeValueWithIncDecButton(1)}> <IconButton
<AddIcon sx={{ size="small"
color: 'white' onClick={() => changeValueWithIncDecButton(1)}
}} />{" "} >
<AddIcon
sx={{
color: 'white',
}}
/>{' '}
</IconButton> </IconButton>
<IconButton size="small" onClick={() => changeValueWithIncDecButton(-1)}> <IconButton
<RemoveIcon sx={{ size="small"
color: 'white' onClick={() => changeValueWithIncDecButton(-1)}
}} />{" "} >
<RemoveIcon
sx={{
color: 'white',
}}
/>{' '}
</IconButton> </IconButton>
</InputAdornment> </InputAdornment>
) : ( ) : (
<></> <></>
), ),
}} }}
onChange={e => listeners(e as eventType)} onChange={(e) => listeners(e as eventType)}
onBlur={e => { onBlur={(e) => {
formatValueOnBlur(e as eventType); formatValueOnBlur(e as eventType);
}} }}
autoComplete="off" autoComplete="off"

View File

@ -1,57 +1,57 @@
import React, { useState } from "react"; import React, { useState } from 'react';
import QRCode from "react-qr-code"; import QRCode from 'react-qr-code';
import { TextP } from "../App-styles"; import { TextP } from '../styles/App-styles';
import { Box, Typography } from "@mui/material"; import { Box, Typography } from '@mui/material';
export const AddressQRCode = ({ targetAddress }) => { export const AddressQRCode = ({ targetAddress }) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
return ( return (
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
gap: "10px", gap: '10px',
alignItems: "center", alignItems: 'center',
flexDirection: "column", flexDirection: 'column',
marginTop: '10px' marginTop: '10px',
}} }}
> >
<Typography <Typography
sx={{ sx={{
cursor: "pointer", cursor: 'pointer',
fontSize: "14px", fontSize: '14px',
}} }}
onClick={() => { onClick={() => {
setOpen((prev)=> !prev); setOpen((prev) => !prev);
}} }}
> >
{open ? 'Hide QR code' :'See QR code'} {open ? 'Hide QR code' : 'See QR code'}
</Typography> </Typography>
{open && ( {open && (
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
gap: "10px", gap: '10px',
alignItems: "center", alignItems: 'center',
justifyContent: "center", justifyContent: 'center',
width: "100%", width: '100%',
}} }}
> >
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
gap: "10px", gap: '10px',
width: "100%", width: '100%',
alignItems: "center", alignItems: 'center',
flexDirection: "column", flexDirection: 'column',
marginTop: "20px", marginTop: '20px',
}} }}
> >
<TextP <TextP
sx={{ sx={{
textAlign: "center", textAlign: 'center',
lineHeight: 1.2, lineHeight: 1.2,
fontSize: "16px", fontSize: '16px',
fontWeight: 500, fontWeight: 500,
}} }}
> >

View File

@ -15,7 +15,8 @@ import { SortablePinnedApps } from './SortablePinnedApps';
import { extractComponents } from '../Chat/MessageDisplay'; import { extractComponents } from '../Chat/MessageDisplay';
import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward'; import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward';
import { AppsPrivate } from './AppsPrivate'; import { AppsPrivate } from './AppsPrivate';
import ThemeSelector from '../../styles/ThemeSelector'; import ThemeSelector from '../Theme/ThemeSelector';
export const AppsHomeDesktop = ({ export const AppsHomeDesktop = ({
setMode, setMode,
myApp, myApp,

View File

@ -1,18 +1,32 @@
import React, { useMemo, useRef, useState } from "react"; import React, { useMemo, useRef, useState } from 'react';
import TipTap from "./TipTap"; import TipTap from './TipTap';
import { AuthenticatedContainerInnerTop, CustomButton } from "../../App-styles"; import {
import { Box, CircularProgress } from "@mui/material"; AuthenticatedContainerInnerTop,
import { objectToBase64 } from "../../qdn/encryption/group-encryption"; CustomButton,
import ShortUniqueId from "short-unique-id"; } from '../../styles/App-styles';
import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar"; import { Box, CircularProgress } from '@mui/material';
import { getBaseApi, getFee } from "../../background"; import { objectToBase64 } from '../../qdn/encryption/group-encryption';
import { decryptPublishes, getTempPublish, handleUnencryptedPublishes, saveTempPublish } from "./GroupAnnouncements"; import ShortUniqueId from 'short-unique-id';
import { AnnouncementList } from "./AnnouncementList"; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
import { Spacer } from "../../common/Spacer"; 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 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 }); const uid = new ShortUniqueId({ length: 8 });
export const AnnouncementDiscussion = ({ export const AnnouncementDiscussion = ({
@ -23,28 +37,28 @@ export const AnnouncementDiscussion = ({
setSelectedAnnouncement, setSelectedAnnouncement,
show, show,
myName, myName,
isPrivate isPrivate,
}) => { }) => {
const [isSending, setIsSending] = useState(false); const [isSending, setIsSending] = useState(false);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [isFocusedParent, setIsFocusedParent] = useState(false); const [isFocusedParent, setIsFocusedParent] = useState(false);
const [comments, setComments] = useState([]) const [comments, setComments] = useState([]);
const [tempPublishedList, setTempPublishedList] = useState([]) const [tempPublishedList, setTempPublishedList] = useState([]);
const firstMountRef = useRef(false) const firstMountRef = useRef(false);
const [data, setData] = useState({}) const [data, setData] = useState({});
const editorRef = useRef(null); const editorRef = useRef(null);
const setEditorRef = (editorInstance) => { const setEditorRef = (editorInstance) => {
editorRef.current = editorInstance; editorRef.current = editorInstance;
}; };
const clearEditorContent = () => { const clearEditorContent = () => {
if (editorRef.current) { if (editorRef.current) {
editorRef.current.chain().focus().clearContent().run(); editorRef.current.chain().focus().clearContent().run();
if(isMobile){ if (isMobile) {
setTimeout(() => { setTimeout(() => {
editorRef.current?.chain().blur().run(); editorRef.current?.chain().blur().run();
setIsFocusedParent(false) setIsFocusedParent(false);
}, 200); }, 200);
} }
} }
@ -52,14 +66,16 @@ export const AnnouncementDiscussion = ({
const getData = async ({ identifier, name }, isPrivate) => { const getData = async ({ identifier, name }, isPrivate) => {
try { try {
const res = await fetch( const res = await fetch(
`${getBaseApiReact()}/arbitrary/DOCUMENT/${name}/${identifier}?encoding=base64` `${getBaseApiReact()}/arbitrary/DOCUMENT/${name}/${identifier}?encoding=base64`
); );
if(!res?.ok) return if (!res?.ok) return;
const data = await res.text(); 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]; const messageData = response[0];
setData((prev) => { setData((prev) => {
return { return {
@ -67,19 +83,19 @@ export const AnnouncementDiscussion = ({
[`${identifier}-${name}`]: messageData, [`${identifier}-${name}`]: messageData,
}; };
}); });
} catch (error) {} } catch (error) {}
}; };
const publishAnc = async ({ encryptedData, identifier }: any) => { const publishAnc = async ({ encryptedData, identifier }: any) => {
try { try {
if (!selectedAnnouncement) return; if (!selectedAnnouncement) return;
return new Promise((res, rej) => { return new Promise((res, rej) => {
window.sendMessage("publishGroupEncryptedResource", { window
encryptedData, .sendMessage('publishGroupEncryptedResource', {
identifier, encryptedData,
}) identifier,
})
.then((response) => { .then((response) => {
if (!response?.error) { if (!response?.error) {
res(response); res(response);
@ -88,63 +104,60 @@ export const AnnouncementDiscussion = ({
rej(response.error); rej(response.error);
}) })
.catch((error) => { .catch((error) => {
rej(error.message || "An error occurred"); rej(error.message || 'An error occurred');
}); });
}); });
} catch (error) {} } catch (error) {}
}; };
const setTempData = async ()=> { const setTempData = async () => {
try { try {
const getTempAnnouncements = await getTempPublish() const getTempAnnouncements = await getTempPublish();
if(getTempAnnouncements[tempKey]){ if (getTempAnnouncements[tempKey]) {
let tempData = [] let tempData = [];
Object.keys(getTempAnnouncements[tempKey] || {}).map((key)=> { Object.keys(getTempAnnouncements[tempKey] || {}).map((key) => {
const value = getTempAnnouncements[tempKey][key] const value = getTempAnnouncements[tempKey][key];
if(value.data?.announcementId === selectedAnnouncement.identifier){ if (value.data?.announcementId === selectedAnnouncement.identifier) {
tempData.push(value.data) tempData.push(value.data);
}
});
setTempPublishedList(tempData);
} }
}) } catch (error) {}
setTempPublishedList(tempData) };
}
} catch (error) {
}
}
const publishComment = async () => { const publishComment = async () => {
try { try {
pauseAllQueues() pauseAllQueues();
const fee = await getFee('ARBITRARY') const fee = await getFee('ARBITRARY');
await show({ await show({
message: "Would you like to perform a ARBITRARY transaction?" , message: 'Would you like to perform a ARBITRARY transaction?',
publishFee: fee.fee + ' QORT' publishFee: fee.fee + ' QORT',
}) });
if (isSending) return; if (isSending) return;
if (editorRef.current) { if (editorRef.current) {
const htmlContent = editorRef.current.getHTML(); const htmlContent = editorRef.current.getHTML();
if (!htmlContent?.trim() || htmlContent?.trim() === "<p></p>") return; if (!htmlContent?.trim() || htmlContent?.trim() === '<p></p>') return;
setIsSending(true); setIsSending(true);
const message = { const message = {
version: 1, version: 1,
extra: {}, extra: {},
message: htmlContent, message: htmlContent,
}; };
const secretKeyObject = isPrivate === false ? null : await getSecretKey(false, true); const secretKeyObject =
const message64: any = await objectToBase64(message); isPrivate === false ? null : await getSecretKey(false, true);
const message64: any = await objectToBase64(message);
const encryptSingle = isPrivate === false ? message64 : await encryptChatMessage(
message64, const encryptSingle =
secretKeyObject isPrivate === false
); ? message64
: await encryptChatMessage(message64, secretKeyObject);
const randomUid = uid.rnd(); const randomUid = uid.rnd();
const identifier = `cm-${selectedAnnouncement.identifier}-${randomUid}`; const identifier = `cm-${selectedAnnouncement.identifier}-${randomUid}`;
const res = await publishAnc({ const res = await publishAnc({
encryptedData: encryptSingle, encryptedData: encryptSingle,
identifier identifier,
}); });
const dataToSaveToStorage = { const dataToSaveToStorage = {
@ -153,18 +166,18 @@ export const AnnouncementDiscussion = ({
service: 'DOCUMENT', service: 'DOCUMENT',
tempData: message, tempData: message,
created: Date.now(), created: Date.now(),
announcementId: selectedAnnouncement.identifier announcementId: selectedAnnouncement.identifier,
} };
await saveTempPublish({data: dataToSaveToStorage, key: tempKey}) await saveTempPublish({ data: dataToSaveToStorage, key: tempKey });
setTempData() setTempData();
clearEditorContent(); clearEditorContent();
} }
// send chat message // send chat message
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} finally { } finally {
resumeAllQueues() resumeAllQueues();
setIsSending(false); setIsSending(false);
} }
}; };
@ -172,7 +185,6 @@ export const AnnouncementDiscussion = ({
const getComments = React.useCallback( const getComments = React.useCallback(
async (selectedAnnouncement, isPrivate) => { async (selectedAnnouncement, isPrivate) => {
try { try {
setIsLoading(true); setIsLoading(true);
const offset = 0; const offset = 0;
@ -181,13 +193,13 @@ export const AnnouncementDiscussion = ({
const identifier = `cm-${selectedAnnouncement.identifier}`; 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 url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`;
const response = await fetch(url, { const response = await fetch(url, {
method: "GET", method: 'GET',
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
}, },
}); });
const responseData = await response.json(); const responseData = await response.json();
setTempData() setTempData();
setComments(responseData); setComments(responseData);
setIsLoading(false); setIsLoading(false);
for (const data of responseData) { for (const data of responseData) {
@ -203,119 +215,122 @@ export const AnnouncementDiscussion = ({
[secretKey] [secretKey]
); );
const loadMore = async()=> { const loadMore = async () => {
try { try {
setIsLoading(true); setIsLoading(true);
const offset = comments.length const offset = comments.length;
const identifier = `cm-${selectedAnnouncement.identifier}`; 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 url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`;
const response = await fetch(url, { const response = await fetch(url, {
method: "GET", method: 'GET',
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
}, },
}); });
const responseData = await response.json(); const responseData = await response.json();
setComments((prev)=> [...prev, ...responseData]); setComments((prev) => [...prev, ...responseData]);
setIsLoading(false); setIsLoading(false);
for (const data of responseData) { for (const data of responseData) {
getData({ name: data.name, identifier: data.identifier }, isPrivate); getData({ name: data.name, identifier: data.identifier }, isPrivate);
} }
} catch (error) { } catch (error) {}
};
}
}
const combinedListTempAndReal = useMemo(() => { const combinedListTempAndReal = useMemo(() => {
// Combine the two lists // Combine the two lists
const combined = [...tempPublishedList, ...comments]; const combined = [...tempPublishedList, ...comments];
// Remove duplicates based on the "identifier" // Remove duplicates based on the "identifier"
const uniqueItems = new Map(); const uniqueItems = new Map();
combined.forEach(item => { combined.forEach((item) => {
uniqueItems.set(item.identifier, item); // This will overwrite duplicates, keeping the last occurrence 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 // 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; return sortedList;
}, [tempPublishedList, comments]); }, [tempPublishedList, comments]);
React.useEffect(() => { React.useEffect(() => {
if(!secretKey && isPrivate) return if (!secretKey && isPrivate) return;
if (selectedAnnouncement && !firstMountRef.current && isPrivate !== null) { if (selectedAnnouncement && !firstMountRef.current && isPrivate !== null) {
getComments(selectedAnnouncement, isPrivate); getComments(selectedAnnouncement, isPrivate);
firstMountRef.current = true firstMountRef.current = true;
} }
}, [selectedAnnouncement, secretKey, isPrivate]); }, [selectedAnnouncement, secretKey, isPrivate]);
return ( return (
<div <div
style={{ style={{
height: isMobile ? '100%' : "100%", height: isMobile ? '100%' : '100%',
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
width: "100%", width: '100%',
}} }}
> >
<div style={{ <div
position: "relative", style={{
width: "100%", position: 'relative',
display: "flex", width: '100%',
flexDirection: "column", display: 'flex',
flexShrink: 0, flexDirection: 'column',
}}> flexShrink: 0,
}}
<AuthenticatedContainerInnerTop sx={{ >
height: '20px' <AuthenticatedContainerInnerTop
}}> sx={{
<ArrowBackIcon onClick={()=> setSelectedAnnouncement(null)} sx={{ height: '20px',
cursor: 'pointer' }}
}} /> >
</AuthenticatedContainerInnerTop> <ArrowBackIcon
onClick={() => setSelectedAnnouncement(null)}
sx={{
cursor: 'pointer',
}}
/>
</AuthenticatedContainerInnerTop>
<Spacer height="20px" /> <Spacer height="20px" />
</div> </div>
<AnnouncementList <AnnouncementList
announcementData={data} announcementData={data}
initialMessages={combinedListTempAndReal} initialMessages={combinedListTempAndReal}
setSelectedAnnouncement={()=> {}} setSelectedAnnouncement={() => {}}
disableComment disableComment
showLoadMore={comments.length > 0 && comments.length % 20 === 0} showLoadMore={comments.length > 0 && comments.length % 20 === 0}
loadMore={loadMore} loadMore={loadMore}
myName={myName} myName={myName}
/> />
<div <div
style={{ style={{
// position: 'fixed', // position: 'fixed',
// bottom: '0px', // bottom: '0px',
backgroundColor: "#232428", backgroundColor: '#232428',
minHeight: isMobile ? "0px" : "150px", minHeight: isMobile ? '0px' : '150px',
maxHeight: isMobile ? "auto" : "400px", maxHeight: isMobile ? 'auto' : '400px',
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
overflow: "hidden", overflow: 'hidden',
width: "100%", width: '100%',
boxSizing: "border-box", boxSizing: 'border-box',
padding: isMobile ? "10px": "20px", padding: isMobile ? '10px' : '20px',
position: isFocusedParent ? 'fixed' : 'relative', position: isFocusedParent ? 'fixed' : 'relative',
bottom: isFocusedParent ? '0px' : 'unset', bottom: isFocusedParent ? '0px' : 'unset',
top: isFocusedParent ? '0px' : 'unset', top: isFocusedParent ? '0px' : 'unset',
zIndex: isFocusedParent ? 5 : 'unset', zIndex: isFocusedParent ? 5 : 'unset',
flexShrink:0, flexShrink: 0,
}} }}
> >
<div <div
style={{ style={{
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
// height: '100%', // height: '100%',
flexGrow: isMobile && 1, flexGrow: isMobile && 1,
overflow: "auto", overflow: 'auto',
}} }}
> >
<TipTap <TipTap
@ -323,79 +338,78 @@ export const AnnouncementDiscussion = ({
onEnter={publishComment} onEnter={publishComment}
disableEnter disableEnter
maxHeightOffset="60px" maxHeightOffset="60px"
isFocusedParent={isFocusedParent} setIsFocusedParent={setIsFocusedParent} isFocusedParent={isFocusedParent}
setIsFocusedParent={setIsFocusedParent}
/> />
</div> </div>
<Box sx={{ <Box
display: 'flex', sx={{
width: '100&', display: 'flex',
gap: '10px', width: '100&',
justifyContent: 'center', gap: '10px',
flexShrink: 0, justifyContent: 'center',
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)",
flexShrink: 0, flexShrink: 0,
padding: isMobile && '5px', position: 'relative',
fontSize: isMobile && '14px'
}} }}
> >
{isSending && ( {isFocusedParent && (
<CircularProgress <CustomButton
size={18} onClick={() => {
sx={{ if (isSending) return;
position: "absolute", setIsFocusedParent(false);
top: "50%", clearEditorContent();
left: "50%", // Unfocus the editor
marginTop: "-12px",
marginLeft: "-12px",
color: "white",
}} }}
/> style={{
marginTop: 'auto',
alignSelf: 'center',
cursor: isSending ? 'default' : 'pointer',
flexShrink: 0,
padding: isMobile && '5px',
fontSize: isMobile && '14px',
background: 'red',
}}
>
{` Close`}
</CustomButton>
)} )}
{` Publish Comment`} <CustomButton
</CustomButton> onClick={() => {
if (isSending) return;
</Box> 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> </div>
<LoadingSnackbar <LoadingSnackbar
open={isLoading} open={isLoading}
info={{ info={{
message: "Loading comments... please wait.", message: 'Loading comments... please wait.',
}} }}
/> />
</div> </div>

View File

@ -1,13 +1,13 @@
import React, { useCallback, useState, useEffect, useRef } from "react"; import React, { useCallback, useState, useEffect, useRef } from 'react';
import { import {
List, List,
AutoSizer, AutoSizer,
CellMeasurerCache, CellMeasurerCache,
CellMeasurer, CellMeasurer,
} from "react-virtualized"; } from 'react-virtualized';
import { AnnouncementItem } from "./AnnouncementItem"; import { AnnouncementItem } from './AnnouncementItem';
import { Box } from "@mui/material"; import { Box } from '@mui/material';
import { CustomButton } from "../../App-styles"; import { CustomButton } from '../../styles/App-styles';
const cache = new CellMeasurerCache({ const cache = new CellMeasurerCache({
fixedWidth: true, fixedWidth: true,
@ -21,9 +21,8 @@ export const AnnouncementList = ({
disableComment, disableComment,
showLoadMore, showLoadMore,
loadMore, loadMore,
myName myName,
}) => { }) => {
const listRef = useRef(); const listRef = useRef();
const [messages, setMessages] = useState(initialMessages); const [messages, setMessages] = useState(initialMessages);
@ -35,39 +34,44 @@ export const AnnouncementList = ({
setMessages(initialMessages); setMessages(initialMessages);
}, [initialMessages]); }, [initialMessages]);
return ( return (
<div <div
style={{ style={{
position: "relative", position: 'relative',
flexGrow: 1, flexGrow: 1,
width: "100%", width: '100%',
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
flexShrink: 1, flexShrink: 1,
overflow: 'auto' overflow: 'auto',
}} }}
> >
{messages.map((message) => { {messages.map((message) => {
const messageData = message?.tempData ? { const messageData = message?.tempData
decryptedData: message?.tempData ? {
} : announcementData[`${message.identifier}-${message.name}`]; decryptedData: message?.tempData,
}
: announcementData[`${message.identifier}-${message.name}`];
return ( return (
<div
<div key={message?.identifier}
key={message?.identifier} style={{
style={{ marginBottom: '10px',
marginBottom: "10px", width: '100%',
width: "100%", display: 'flex',
display: "flex", flexDirection: 'column',
flexDirection: "column", alignItems: 'center',
alignItems: "center", }}
}} >
> <AnnouncementItem
<AnnouncementItem myName={myName} disableComment={disableComment} setSelectedAnnouncement={setSelectedAnnouncement} message={message} messageData={messageData} /> myName={myName}
</div> disableComment={disableComment}
setSelectedAnnouncement={setSelectedAnnouncement}
message={message}
messageData={messageData}
/>
</div>
); );
})} })}
{/* <AutoSizer> {/* <AutoSizer>
@ -83,16 +87,20 @@ export const AnnouncementList = ({
/> />
)} )}
</AutoSizer> */} </AutoSizer> */}
<Box sx={{ <Box
width: '100%', sx={{
marginTop: '25px', width: '100%',
display: 'flex', marginTop: '25px',
justifyContent: 'center' display: 'flex',
}}> justifyContent: 'center',
{showLoadMore && ( }}
<CustomButton onClick={loadMore}>Load older announcements</CustomButton> >
)} {showLoadMore && (
</Box> <CustomButton onClick={loadMore}>
Load older announcements
</CustomButton>
)}
</Box>
</div> </div>
); );
}; };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,30 +4,33 @@ import React, {
useMemo, useMemo,
useRef, useRef,
useState, useState,
} from "react"; } from 'react';
import { CreateCommonSecret } from "./CreateCommonSecret"; import { CreateCommonSecret } from './CreateCommonSecret';
import { reusableGet } from "../../qdn/publish/pubish"; import { reusableGet } from '../../qdn/publish/pubish';
import { uint8ArrayToObject } from "../../backgroundFunctions/encryption"; import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
import { import {
base64ToUint8Array, base64ToUint8Array,
objectToBase64, objectToBase64,
} from "../../qdn/encryption/group-encryption"; } from '../../qdn/encryption/group-encryption';
import { ChatContainerComp } from "./ChatContainer"; import { ChatContainerComp } from './ChatContainer';
import { ChatList } from "./ChatList"; import { ChatList } from './ChatList';
import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css"; import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css';
import Tiptap from "./TipTap"; import Tiptap from './TipTap';
import { AuthenticatedContainerInnerTop, CustomButton } from "../../App-styles"; import {
import CircularProgress from "@mui/material/CircularProgress"; AuthenticatedContainerInnerTop,
import { getBaseApi, getFee } from "../../background"; CustomButton,
import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar"; } from '../../styles/App-styles';
import { Box, Typography } from "@mui/material"; import CircularProgress from '@mui/material/CircularProgress';
import { Spacer } from "../../common/Spacer"; import { getBaseApi, getFee } from '../../background';
import ShortUniqueId from "short-unique-id"; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
import { AnnouncementList } from "./AnnouncementList"; 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 }); const uid = new ShortUniqueId({ length: 8 });
import CampaignIcon from "@mui/icons-material/Campaign"; import CampaignIcon from '@mui/icons-material/Campaign';
import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import { AnnouncementDiscussion } from "./AnnouncementDiscussion"; import { AnnouncementDiscussion } from './AnnouncementDiscussion';
import { import {
MyContext, MyContext,
getArbitraryEndpointReact, getArbitraryEndpointReact,
@ -35,11 +38,11 @@ import {
isMobile, isMobile,
pauseAllQueues, pauseAllQueues,
resumeAllQueues, resumeAllQueues,
} from "../../App"; } from '../../App';
import { RequestQueueWithPromise } from "../../utils/queue/queue"; import { RequestQueueWithPromise } from '../../utils/queue/queue';
import { CustomizedSnackbars } from "../Snackbar/Snackbar"; import { CustomizedSnackbars } from '../Snackbar/Snackbar';
import { addDataPublishesFunc, getDataPublishesFunc } from "../Group/Group"; import { addDataPublishesFunc, getDataPublishesFunc } from '../Group/Group';
import { getRootHeight } from "../../utils/mobile/mobileUtils"; import { getRootHeight } from '../../utils/mobile/mobileUtils';
export const requestQueueCommentCount = new RequestQueueWithPromise(3); export const requestQueueCommentCount = new RequestQueueWithPromise(3);
export const requestQueuePublishedAccouncements = new RequestQueueWithPromise( export const requestQueuePublishedAccouncements = new RequestQueueWithPromise(
@ -48,10 +51,11 @@ export const requestQueuePublishedAccouncements = new RequestQueueWithPromise(
export const saveTempPublish = async ({ data, key }: any) => { export const saveTempPublish = async ({ data, key }: any) => {
return new Promise((res, rej) => { return new Promise((res, rej) => {
window.sendMessage("saveTempPublish", { window
data, .sendMessage('saveTempPublish', {
key, data,
}) key,
})
.then((response) => { .then((response) => {
if (!response?.error) { if (!response?.error) {
res(response); res(response);
@ -60,37 +64,37 @@ export const saveTempPublish = async ({ data, key }: any) => {
rej(response.error); rej(response.error);
}) })
.catch((error) => { .catch((error) => {
rej(error.message || "An error occurred"); rej(error.message || 'An error occurred');
}); });
}); });
}; };
export const getTempPublish = async () => { export const getTempPublish = async () => {
return new Promise((res, rej) => { return new Promise((res, rej) => {
window.sendMessage("getTempPublish", {}) window
.then((response) => { .sendMessage('getTempPublish', {})
if (!response?.error) { .then((response) => {
res(response); if (!response?.error) {
return; res(response);
} return;
rej(response.error); }
}) rej(response.error);
.catch((error) => { })
rej(error.message || "An error occurred"); .catch((error) => {
}); rej(error.message || 'An error occurred');
});
}); });
}; };
export const decryptPublishes = async (encryptedMessages: any[], secretKey) => { export const decryptPublishes = async (encryptedMessages: any[], secretKey) => {
try { try {
return await new Promise((res, rej) => { return await new Promise((res, rej) => {
window.sendMessage("decryptSingleForPublishes", { window
data: encryptedMessages, .sendMessage('decryptSingleForPublishes', {
secretKeyObject: secretKey, data: encryptedMessages,
skipDecodeBase64: true, secretKeyObject: secretKey,
}) skipDecodeBase64: true,
})
.then((response) => { .then((response) => {
if (!response?.error) { if (!response?.error) {
res(response); res(response);
@ -99,26 +103,23 @@ export const decryptPublishes = async (encryptedMessages: any[], secretKey) => {
rej(response.error); rej(response.error);
}) })
.catch((error) => { .catch((error) => {
rej(error.message || "An error occurred"); rej(error.message || 'An error occurred');
}); });
}); });
} catch (error) {} } catch (error) {}
}; };
export const handleUnencryptedPublishes = (publishes) => { export const handleUnencryptedPublishes = (publishes) => {
let publishesData = [] let publishesData = [];
publishes.forEach((pub)=> { publishes.forEach((pub) => {
try { try {
const decryptToUnit8Array = base64ToUint8Array(pub); const decryptToUnit8Array = base64ToUint8Array(pub);
const decodedData = uint8ArrayToObject(decryptToUnit8Array); const decodedData = uint8ArrayToObject(decryptToUnit8Array);
if(decodedData){ if (decodedData) {
publishesData.push({decryptedData: decodedData}) publishesData.push({ decryptedData: decodedData });
} }
} catch (error) { } catch (error) {}
});
} return publishesData;
})
return publishesData
}; };
export const GroupAnnouncements = ({ export const GroupAnnouncements = ({
selectedGroup, selectedGroup,
@ -130,7 +131,7 @@ export const GroupAnnouncements = ({
isAdmin, isAdmin,
hide, hide,
myName, myName,
isPrivate isPrivate,
}) => { }) => {
const [messages, setMessages] = useState([]); const [messages, setMessages] = useState([]);
const [isSending, setIsSending] = useState(false); const [isSending, setIsSending] = useState(false);
@ -159,12 +160,15 @@ export const GroupAnnouncements = ({
useEffect(() => { useEffect(() => {
if (!selectedGroup) return; if (!selectedGroup) return;
(async () => { (async () => {
const res = await getDataPublishesFunc(selectedGroup, "anc"); const res = await getDataPublishesFunc(selectedGroup, 'anc');
dataPublishes.current = res || {}; dataPublishes.current = res || {};
})(); })();
}, [selectedGroup]); }, [selectedGroup]);
const getAnnouncementData = async ({ identifier, name, resource }, isPrivate) => { const getAnnouncementData = async (
{ identifier, name, resource },
isPrivate
) => {
try { try {
let data = dataPublishes.current[`${name}-${identifier}`]; let data = dataPublishes.current[`${name}-${identifier}`];
if ( if (
@ -179,14 +183,17 @@ export const GroupAnnouncements = ({
}); });
if (!res?.ok) return; if (!res?.ok) return;
data = await res.text(); data = await res.text();
await addDataPublishesFunc({ ...resource, data }, selectedGroup, "anc"); await addDataPublishesFunc({ ...resource, data }, selectedGroup, 'anc');
} else { } else {
data = data.data; 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]; const messageData = response[0];
if(!messageData) return if (!messageData) return;
setAnnouncementData((prev) => { setAnnouncementData((prev) => {
return { return {
...prev, ...prev,
@ -194,12 +201,17 @@ export const GroupAnnouncements = ({
}; };
}); });
} catch (error) { } catch (error) {
console.error("error", error); console.error('error', error);
} }
}; };
useEffect(() => { useEffect(() => {
if ((!secretKey && isPrivate) || hasInitializedWebsocket.current || isPrivate === null) return; if (
(!secretKey && isPrivate) ||
hasInitializedWebsocket.current ||
isPrivate === null
)
return;
setIsLoading(true); setIsLoading(true);
// initWebsocketMessageGroup() // initWebsocketMessageGroup()
hasInitializedWebsocket.current = true; hasInitializedWebsocket.current = true;
@ -208,10 +220,11 @@ export const GroupAnnouncements = ({
const encryptChatMessage = async (data: string, secretKeyObject: any) => { const encryptChatMessage = async (data: string, secretKeyObject: any) => {
try { try {
return new Promise((res, rej) => { return new Promise((res, rej) => {
window.sendMessage("encryptSingle", { window
data, .sendMessage('encryptSingle', {
secretKeyObject, data,
}) secretKeyObject,
})
.then((response) => { .then((response) => {
if (!response?.error) { if (!response?.error) {
res(response); res(response);
@ -220,19 +233,19 @@ export const GroupAnnouncements = ({
rej(response.error); rej(response.error);
}) })
.catch((error) => { .catch((error) => {
rej(error.message || "An error occurred"); rej(error.message || 'An error occurred');
}); });
}); });
} catch (error) {} } catch (error) {}
}; };
const publishAnc = async ({ encryptedData, identifier }: any) => { const publishAnc = async ({ encryptedData, identifier }: any) => {
return new Promise((res, rej) => { return new Promise((res, rej) => {
window.sendMessage("publishGroupEncryptedResource", { window
encryptedData, .sendMessage('publishGroupEncryptedResource', {
identifier, encryptedData,
}) identifier,
})
.then((response) => { .then((response) => {
if (!response?.error) { if (!response?.error) {
res(response); res(response);
@ -241,9 +254,8 @@ export const GroupAnnouncements = ({
rej(response.error); rej(response.error);
}) })
.catch((error) => { .catch((error) => {
rej(error.message || "An error occurred"); rej(error.message || 'An error occurred');
}); });
}); });
}; };
const clearEditorContent = () => { const clearEditorContent = () => {
@ -255,7 +267,7 @@ export const GroupAnnouncements = ({
setIsFocusedParent(false); setIsFocusedParent(false);
setTimeout(() => { setTimeout(() => {
triggerRerender(); triggerRerender();
}, 300); }, 300);
}, 200); }, 200);
} }
} }
@ -266,10 +278,12 @@ export const GroupAnnouncements = ({
const getTempAnnouncements = await getTempPublish(); const getTempAnnouncements = await getTempPublish();
if (getTempAnnouncements?.announcement) { if (getTempAnnouncements?.announcement) {
let tempData = []; let tempData = [];
Object.keys(getTempAnnouncements?.announcement || {}).filter((annKey)=> annKey?.startsWith(`grp-${selectedGroup}-anc`)).map((key) => { Object.keys(getTempAnnouncements?.announcement || {})
const value = getTempAnnouncements?.announcement[key]; .filter((annKey) => annKey?.startsWith(`grp-${selectedGroup}-anc`))
tempData.push(value.data); .map((key) => {
}); const value = getTempAnnouncements?.announcement[key];
tempData.push(value.data);
});
setTempPublishedList(tempData); setTempPublishedList(tempData);
} }
} catch (error) {} } catch (error) {}
@ -278,27 +292,28 @@ export const GroupAnnouncements = ({
const publishAnnouncement = async () => { const publishAnnouncement = async () => {
try { try {
pauseAllQueues(); pauseAllQueues();
const fee = await getFee("ARBITRARY"); const fee = await getFee('ARBITRARY');
await show({ await show({
message: "Would you like to perform a ARBITRARY transaction?", message: 'Would you like to perform a ARBITRARY transaction?',
publishFee: fee.fee + " QORT", publishFee: fee.fee + ' QORT',
}); });
if (isSending) return; if (isSending) return;
if (editorRef.current) { if (editorRef.current) {
const htmlContent = editorRef.current.getHTML(); const htmlContent = editorRef.current.getHTML();
if (!htmlContent?.trim() || htmlContent?.trim() === "<p></p>") return; if (!htmlContent?.trim() || htmlContent?.trim() === '<p></p>') return;
setIsSending(true); setIsSending(true);
const message = { const message = {
version: 1, version: 1,
extra: {}, extra: {},
message: htmlContent, message: htmlContent,
}; };
const secretKeyObject = isPrivate === false ? null : await getSecretKey(false, true); const secretKeyObject =
const message64: any = await objectToBase64(message); isPrivate === false ? null : await getSecretKey(false, true);
const encryptSingle = isPrivate === false ? message64 : await encryptChatMessage( const message64: any = await objectToBase64(message);
message64, const encryptSingle =
secretKeyObject isPrivate === false
); ? message64
: await encryptChatMessage(message64, secretKeyObject);
const randomUid = uid.rnd(); const randomUid = uid.rnd();
const identifier = `grp-${selectedGroup}-anc-${randomUid}`; const identifier = `grp-${selectedGroup}-anc-${randomUid}`;
const res = await publishAnc({ const res = await publishAnc({
@ -309,13 +324,13 @@ export const GroupAnnouncements = ({
const dataToSaveToStorage = { const dataToSaveToStorage = {
name: myName, name: myName,
identifier, identifier,
service: "DOCUMENT", service: 'DOCUMENT',
tempData: message, tempData: message,
created: Date.now(), created: Date.now(),
}; };
await saveTempPublish({ await saveTempPublish({
data: dataToSaveToStorage, data: dataToSaveToStorage,
key: "announcement", key: 'announcement',
}); });
setTempData(selectedGroup); setTempData(selectedGroup);
clearEditorContent(); clearEditorContent();
@ -324,7 +339,7 @@ export const GroupAnnouncements = ({
} catch (error) { } catch (error) {
if (!error) return; if (!error) return;
setInfoSnack({ setInfoSnack({
type: "error", type: 'error',
message: error, message: error,
}); });
setOpenSnack(true); setOpenSnack(true);
@ -343,9 +358,9 @@ export const GroupAnnouncements = ({
const identifier = `grp-${selectedGroup}-anc-`; 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 url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`;
const response = await fetch(url, { const response = await fetch(url, {
method: "GET", method: 'GET',
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
}, },
}); });
const responseData = await response.json(); const responseData = await response.json();
@ -354,11 +369,14 @@ export const GroupAnnouncements = ({
setAnnouncements(responseData); setAnnouncements(responseData);
setIsLoading(false); setIsLoading(false);
for (const data of responseData) { for (const data of responseData) {
getAnnouncementData({ getAnnouncementData(
name: data.name, {
identifier: data.identifier, name: data.name,
resource: data, identifier: data.identifier,
}, isPrivate); resource: data,
},
isPrivate
);
} }
} catch (error) { } catch (error) {
} finally { } finally {
@ -369,8 +387,13 @@ export const GroupAnnouncements = ({
); );
React.useEffect(() => { React.useEffect(() => {
if(!secretKey && isPrivate) return if (!secretKey && isPrivate) return;
if (selectedGroup && !hasInitialized.current && !hide && isPrivate !== null) { if (
selectedGroup &&
!hasInitialized.current &&
!hide &&
isPrivate !== null
) {
getAnnouncements(selectedGroup, isPrivate); getAnnouncements(selectedGroup, isPrivate);
hasInitialized.current = true; hasInitialized.current = true;
} }
@ -384,9 +407,9 @@ export const GroupAnnouncements = ({
const identifier = `grp-${selectedGroup}-anc-`; 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 url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`;
const response = await fetch(url, { const response = await fetch(url, {
method: "GET", method: 'GET',
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
}, },
}); });
const responseData = await response.json(); const responseData = await response.json();
@ -394,7 +417,10 @@ export const GroupAnnouncements = ({
setAnnouncements((prev) => [...prev, ...responseData]); setAnnouncements((prev) => [...prev, ...responseData]);
setIsLoading(false); setIsLoading(false);
for (const data of responseData) { for (const data of responseData) {
getAnnouncementData({ name: data.name, identifier: data.identifier }, isPrivate); getAnnouncementData(
{ name: data.name, identifier: data.identifier },
isPrivate
);
} }
} catch (error) {} } catch (error) {}
}; };
@ -406,9 +432,9 @@ export const GroupAnnouncements = ({
const identifier = `grp-${selectedGroup}-anc-`; 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 url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${0}&reverse=true&prefix=true`;
const response = await fetch(url, { const response = await fetch(url, {
method: "GET", method: 'GET',
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
}, },
}); });
const responseData = await response.json(); const responseData = await response.json();
@ -416,10 +442,13 @@ export const GroupAnnouncements = ({
if (!latestMessage) { if (!latestMessage) {
for (const data of responseData) { for (const data of responseData) {
try { try {
getAnnouncementData({ getAnnouncementData(
name: data.name, {
identifier: data.identifier, name: data.name,
}, isPrivate); identifier: data.identifier,
},
isPrivate
);
} catch (error) {} } catch (error) {}
} }
setAnnouncements(responseData); setAnnouncements(responseData);
@ -434,7 +463,10 @@ export const GroupAnnouncements = ({
for (const data of newArray) { for (const data of newArray) {
try { try {
getAnnouncementData({ name: data.name, identifier: data.identifier }, isPrivate); getAnnouncementData(
{ name: data.name, identifier: data.identifier },
isPrivate
);
} catch (error) {} } catch (error) {}
} }
setAnnouncements((prev) => [...newArray, ...prev]); setAnnouncements((prev) => [...newArray, ...prev]);
@ -486,13 +518,15 @@ export const GroupAnnouncements = ({
<div <div
style={{ style={{
// reference to change height // reference to change height
height: isMobile ? `calc(${rootHeight} - 127px` : "calc(100vh - 70px)", height: isMobile
display: "flex", ? `calc(${rootHeight} - 127px`
flexDirection: "column", : 'calc(100vh - 70px)',
width: "100%", display: 'flex',
visibility: hide && "hidden", flexDirection: 'column',
position: hide && "fixed", width: '100%',
left: hide && "-1000px", visibility: hide && 'hidden',
position: hide && 'fixed',
left: hide && '-1000px',
}} }}
> >
<AnnouncementDiscussion <AnnouncementDiscussion
@ -509,63 +543,62 @@ export const GroupAnnouncements = ({
); );
} }
return ( return (
<div <div
style={{ style={{
// reference to change height // reference to change height
height: isMobile ? `calc(${rootHeight} - 127px` : "calc(100vh - 70px)", height: isMobile ? `calc(${rootHeight} - 127px` : 'calc(100vh - 70px)',
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
width: "100%", width: '100%',
visibility: hide && "hidden", visibility: hide && 'hidden',
position: hide && "fixed", position: hide && 'fixed',
left: hide && "-1000px", left: hide && '-1000px',
}} }}
> >
<div <div
style={{ style={{
position: "relative", position: 'relative',
width: "100%", width: '100%',
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
flexShrink: 0, flexShrink: 0,
}} }}
> >
{!isMobile && ( {!isMobile && (
<Box <Box
sx={{ sx={{
width: "100%", width: '100%',
display: "flex", display: 'flex',
justifyContent: "center", justifyContent: 'center',
padding: isMobile ? "8px" : "25px", padding: isMobile ? '8px' : '25px',
fontSize: isMobile ? "16px" : "20px", fontSize: isMobile ? '16px' : '20px',
gap: "20px", gap: '20px',
alignItems: "center", alignItems: 'center',
}} }}
> >
<CampaignIcon <CampaignIcon
sx={{ sx={{
fontSize: isMobile ? "16px" : "30px", fontSize: isMobile ? '16px' : '30px',
}} }}
/> />
Group Announcements Group Announcements
</Box> </Box>
)} )}
<Spacer height={isMobile ? "0px" : "25px"} /> <Spacer height={isMobile ? '0px' : '25px'} />
</div> </div>
{!isLoading && combinedListTempAndReal?.length === 0 && ( {!isLoading && combinedListTempAndReal?.length === 0 && (
<Box <Box
sx={{ sx={{
width: "100%", width: '100%',
display: "flex", display: 'flex',
justifyContent: "center", justifyContent: 'center',
}} }}
> >
<Typography <Typography
sx={{ sx={{
fontSize: "16px", fontSize: '16px',
}} }}
> >
No announcements No announcements
@ -589,28 +622,28 @@ export const GroupAnnouncements = ({
style={{ style={{
// position: 'fixed', // position: 'fixed',
// bottom: '0px', // bottom: '0px',
backgroundColor: "#232428", backgroundColor: '#232428',
minHeight: isMobile ? "0px" : "150px", minHeight: isMobile ? '0px' : '150px',
maxHeight: isMobile ? "auto" : "400px", maxHeight: isMobile ? 'auto' : '400px',
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
overflow: "hidden", overflow: 'hidden',
width: "100%", width: '100%',
boxSizing: "border-box", boxSizing: 'border-box',
padding: isMobile ? "10px" : "20px", padding: isMobile ? '10px' : '20px',
position: isFocusedParent ? "fixed" : "relative", position: isFocusedParent ? 'fixed' : 'relative',
bottom: isFocusedParent ? "0px" : "unset", bottom: isFocusedParent ? '0px' : 'unset',
top: isFocusedParent ? "0px" : "unset", top: isFocusedParent ? '0px' : 'unset',
zIndex: isFocusedParent ? 5 : "unset", zIndex: isFocusedParent ? 5 : 'unset',
flexShrink: 0, flexShrink: 0,
}} }}
> >
<div <div
style={{ style={{
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
flexGrow: isMobile && 1, flexGrow: isMobile && 1,
overflow: "auto", overflow: 'auto',
// height: '100%', // height: '100%',
}} }}
> >
@ -625,12 +658,12 @@ export const GroupAnnouncements = ({
</div> </div>
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
width: "100&", width: '100&',
gap: "10px", gap: '10px',
justifyContent: "center", justifyContent: 'center',
flexShrink: 0, flexShrink: 0,
position: "relative", position: 'relative',
}} }}
> >
{isFocusedParent && ( {isFocusedParent && (
@ -639,19 +672,19 @@ export const GroupAnnouncements = ({
if (isSending) return; if (isSending) return;
setIsFocusedParent(false); setIsFocusedParent(false);
clearEditorContent(); clearEditorContent();
setTimeout(() => { setTimeout(() => {
triggerRerender(); triggerRerender();
}, 300); }, 300);
// Unfocus the editor // Unfocus the editor
}} }}
style={{ style={{
marginTop: "auto", marginTop: 'auto',
alignSelf: "center", alignSelf: 'center',
cursor: isSending ? "default" : "pointer", cursor: isSending ? 'default' : 'pointer',
background: "var(--danger)", background: 'var(--danger)',
flexShrink: 0, flexShrink: 0,
padding: isMobile && "5px", padding: isMobile && '5px',
fontSize: isMobile && "14px", fontSize: isMobile && '14px',
}} }}
> >
{` Close`} {` Close`}
@ -663,25 +696,25 @@ export const GroupAnnouncements = ({
publishAnnouncement(); publishAnnouncement();
}} }}
style={{ style={{
marginTop: "auto", marginTop: 'auto',
alignSelf: "center", alignSelf: 'center',
cursor: isSending ? "default" : "pointer", cursor: isSending ? 'default' : 'pointer',
background: isSending && "rgba(0, 0, 0, 0.8)", background: isSending && 'rgba(0, 0, 0, 0.8)',
flexShrink: 0, flexShrink: 0,
padding: isMobile && "5px", padding: isMobile && '5px',
fontSize: isMobile && "14px", fontSize: isMobile && '14px',
}} }}
> >
{isSending && ( {isSending && (
<CircularProgress <CircularProgress
size={18} size={18}
sx={{ sx={{
position: "absolute", position: 'absolute',
top: "50%", top: '50%',
left: "50%", left: '50%',
marginTop: "-12px", marginTop: '-12px',
marginLeft: "-12px", marginLeft: '-12px',
color: "white", color: 'white',
}} }}
/> />
)} )}
@ -701,7 +734,7 @@ export const GroupAnnouncements = ({
<LoadingSnackbar <LoadingSnackbar
open={isLoading} open={isLoading}
info={{ info={{
message: "Loading announcements... please wait.", message: 'Loading announcements... please wait.',
}} }}
/> />
</div> </div>

View File

@ -6,7 +6,7 @@ import { IconWrapper } from './Desktop/DesktopFooter';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { enabledDevModeAtom } from '../atoms/global'; import { enabledDevModeAtom } from '../atoms/global';
import { AppsIcon } from '../assets/Icons/AppsIcon'; import { AppsIcon } from '../assets/Icons/AppsIcon';
import ThemeSelector from '../styles/ThemeSelector'; import ThemeSelector from './Theme/ThemeSelector';
export const DesktopSideBar = ({ export const DesktopSideBar = ({
goToHome, goToHome,

View File

@ -1,5 +1,5 @@
import React, { useContext, useEffect, useMemo, useState } from "react"; import React, { useContext, useEffect, useMemo, useState } from 'react';
import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events"; import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
import { import {
Box, Box,
Button, Button,
@ -9,12 +9,12 @@ import {
DialogActions, DialogActions,
DialogContent, DialogContent,
Typography, Typography,
} from "@mui/material"; } from '@mui/material';
import { CustomButton, CustomButtonAccept } from "../../App-styles"; import { CustomButton, CustomButtonAccept } from '../../styles/App-styles';
import { getBaseApiReact, MyContext } from "../../App"; import { getBaseApiReact, MyContext } from '../../App';
import { getFee } from "../../background"; import { getFee } from '../../background';
import { CustomizedSnackbars } from "../Snackbar/Snackbar"; import { CustomizedSnackbars } from '../Snackbar/Snackbar';
import { FidgetSpinner } from "react-loader-spinner"; import { FidgetSpinner } from 'react-loader-spinner';
export const JoinGroup = ({ memberGroups }) => { export const JoinGroup = ({ memberGroups }) => {
const { show, setTxList } = useContext(MyContext); const { show, setTxList } = useContext(MyContext);
@ -42,43 +42,45 @@ export const JoinGroup = ({ memberGroups }) => {
}; };
useEffect(() => { useEffect(() => {
subscribeToEvent("globalActionJoinGroup", handleJoinGroup); subscribeToEvent('globalActionJoinGroup', handleJoinGroup);
return () => { return () => {
unsubscribeFromEvent("globalActionJoinGroup", handleJoinGroup); unsubscribeFromEvent('globalActionJoinGroup', handleJoinGroup);
}; };
}, []); }, []);
const isInGroup = useMemo(()=> { const isInGroup = useMemo(() => {
return !!memberGroups.find((item)=> +item?.groupId === +groupInfo?.groupId) return !!memberGroups.find(
}, [memberGroups, groupInfo]) (item) => +item?.groupId === +groupInfo?.groupId
);
}, [memberGroups, groupInfo]);
const joinGroup = async (group, isOpen) => { const joinGroup = async (group, isOpen) => {
try { try {
const groupId = group.groupId; const groupId = group.groupId;
const fee = await getFee("JOIN_GROUP"); const fee = await getFee('JOIN_GROUP');
await show({ await show({
message: "Would you like to perform an JOIN_GROUP transaction?", message: 'Would you like to perform an JOIN_GROUP transaction?',
publishFee: fee.fee + " QORT", publishFee: fee.fee + ' QORT',
}); });
setIsLoadingJoinGroup(true); setIsLoadingJoinGroup(true);
await new Promise((res, rej) => { await new Promise((res, rej) => {
window window
.sendMessage("joinGroup", { .sendMessage('joinGroup', {
groupId, groupId,
}) })
.then((response) => { .then((response) => {
if (!response?.error) { if (!response?.error) {
setInfoSnack({ setInfoSnack({
type: "success", type: 'success',
message: 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) { if (isOpen) {
setTxList((prev) => [ setTxList((prev) => [
{ {
...response, ...response,
type: "joined-group", type: 'joined-group',
label: `Joined Group ${group?.groupName}: awaiting confirmation`, label: `Joined Group ${group?.groupName}: awaiting confirmation`,
labelDone: `Joined Group ${group?.groupName}: success!`, labelDone: `Joined Group ${group?.groupName}: success!`,
done: false, done: false,
@ -90,7 +92,7 @@ export const JoinGroup = ({ memberGroups }) => {
setTxList((prev) => [ setTxList((prev) => [
{ {
...response, ...response,
type: "joined-group-request", type: 'joined-group-request',
label: `Requested to join Group ${group?.groupName}: awaiting confirmation`, label: `Requested to join Group ${group?.groupName}: awaiting confirmation`,
labelDone: `Requested to join Group ${group?.groupName}: success!`, labelDone: `Requested to join Group ${group?.groupName}: success!`,
done: false, done: false,
@ -105,7 +107,7 @@ export const JoinGroup = ({ memberGroups }) => {
return; return;
} else { } else {
setInfoSnack({ setInfoSnack({
type: "error", type: 'error',
message: response?.error, message: response?.error,
}); });
setOpenSnack(true); setOpenSnack(true);
@ -114,8 +116,8 @@ export const JoinGroup = ({ memberGroups }) => {
}) })
.catch((error) => { .catch((error) => {
setInfoSnack({ setInfoSnack({
type: "error", type: 'error',
message: error.message || "An error occurred", message: error.message || 'An error occurred',
}); });
setOpenSnack(true); setOpenSnack(true);
rej(error); rej(error);
@ -138,37 +140,37 @@ export const JoinGroup = ({ memberGroups }) => {
{!groupInfo && ( {!groupInfo && (
<Box <Box
sx={{ sx={{
width: "325px", width: '325px',
height: "150px", height: '150px',
display: "flex", display: 'flex',
alignItems: "center", alignItems: 'center',
justifyContent: "center", justifyContent: 'center',
}} }}
> >
{" "} {' '}
<CircularProgress <CircularProgress
size={25} size={25}
sx={{ sx={{
color: "white", color: 'white',
}} }}
/>{" "} />{' '}
</Box> </Box>
)} )}
<Box <Box
sx={{ sx={{
width: "325px", width: '325px',
height: "auto", height: 'auto',
maxHeight: "400px", maxHeight: '400px',
display: !groupInfo ? "none" : "flex", display: !groupInfo ? 'none' : 'flex',
flexDirection: "column", flexDirection: 'column',
alignItems: "center", alignItems: 'center',
gap: "10px", gap: '10px',
padding: "10px", padding: '10px',
}} }}
> >
<Typography <Typography
sx={{ sx={{
fontSize: "15px", fontSize: '15px',
fontWeight: 600, fontWeight: 600,
}} }}
> >
@ -176,7 +178,7 @@ export const JoinGroup = ({ memberGroups }) => {
</Typography> </Typography>
<Typography <Typography
sx={{ sx={{
fontSize: "15px", fontSize: '15px',
fontWeight: 600, fontWeight: 600,
}} }}
> >
@ -185,7 +187,7 @@ export const JoinGroup = ({ memberGroups }) => {
{groupInfo?.description && ( {groupInfo?.description && (
<Typography <Typography
sx={{ sx={{
fontSize: "15px", fontSize: '15px',
fontWeight: 600, fontWeight: 600,
}} }}
> >
@ -193,19 +195,19 @@ export const JoinGroup = ({ memberGroups }) => {
</Typography> </Typography>
)} )}
{isInGroup && ( {isInGroup && (
<Typography <Typography
sx={{ sx={{
fontSize: "14px", fontSize: '14px',
fontWeight: 600, fontWeight: 600,
}} }}
> >
*You are already in this group! *You are already in this group!
</Typography> </Typography>
)} )}
{!isInGroup && groupInfo?.isOpen === false && ( {!isInGroup && groupInfo?.isOpen === false && (
<Typography <Typography
sx={{ sx={{
fontSize: "14px", fontSize: '14px',
fontWeight: 600, fontWeight: 600,
}} }}
> >
@ -216,32 +218,34 @@ export const JoinGroup = ({ memberGroups }) => {
</Box> </Box>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<ButtonBase onClick={() => { <ButtonBase
onClick={() => {
joinGroup(groupInfo, groupInfo?.isOpen); joinGroup(groupInfo, groupInfo?.isOpen);
setIsOpen(false); 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> </ButtonBase>
<CustomButtonAccept <CustomButtonAccept
color="black" color="black"
bgColor="var(--danger)" bgColor="var(--danger)"
sx={{ sx={{
minWidth: "102px", minWidth: '102px',
height: "45px", height: '45px',
}} }}
onClick={() => setIsOpen(false)} onClick={() => setIsOpen(false)}
> >
@ -259,14 +263,14 @@ export const JoinGroup = ({ memberGroups }) => {
{isLoadingJoinGroup && ( {isLoadingJoinGroup && (
<Box <Box
sx={{ sx={{
position: "absolute", position: 'absolute',
top: 0, top: 0,
left: 0, left: 0,
right: 0, right: 0,
bottom: 0, bottom: 0,
display: "flex", display: 'flex',
justifyContent: "center", justifyContent: 'center',
alignItems: "center", alignItems: 'center',
}} }}
> >
<FidgetSpinner <FidgetSpinner

File diff suppressed because it is too large Load Diff

View File

@ -13,29 +13,29 @@ import {
InputLabel, InputLabel,
Snackbar, Snackbar,
Typography, Typography,
} from "@mui/material"; } from '@mui/material';
import React, { import React, {
useCallback, useCallback,
useContext, useContext,
useEffect, useEffect,
useMemo, useMemo,
useState, useState,
} from "react"; } from 'react';
import CloseIcon from "@mui/icons-material/Close"; import CloseIcon from '@mui/icons-material/Close';
import { MyContext, getBaseApiReact } from "../../App"; import { MyContext, getBaseApiReact } from '../../App';
import { import {
executeEvent, executeEvent,
subscribeToEvent, subscribeToEvent,
unsubscribeFromEvent, unsubscribeFromEvent,
} from "../../utils/events"; } from '../../utils/events';
import { getFee, getNameOrAddress } from "../../background"; import { getFee, getNameOrAddress } from '../../background';
import CopyToClipboard from "react-copy-to-clipboard"; import CopyToClipboard from 'react-copy-to-clipboard';
import { AddressBox } from "../../App-styles"; import { AddressBox } from '../../styles/App-styles';
import { Spacer } from "../../common/Spacer"; import { Spacer } from '../../common/Spacer';
import Copy from "../../assets/svgs/Copy.svg"; import Copy from '../../assets/svgs/Copy.svg';
import { Loader } from "../Loader"; import { Loader } from '../Loader';
import { FidgetSpinner } from "react-loader-spinner"; import { FidgetSpinner } from 'react-loader-spinner';
import { useModal } from "../../common/useModal"; import { useModal } from '../../common/useModal';
export const Minting = ({ export const Minting = ({
setIsOpenMinting, setIsOpenMinting,
@ -47,30 +47,30 @@ export const Minting = ({
}) => { }) => {
const [mintingAccounts, setMintingAccounts] = useState([]); const [mintingAccounts, setMintingAccounts] = useState([]);
const [accountInfo, setAccountInfo] = useState(null); const [accountInfo, setAccountInfo] = useState(null);
const [rewardSharePublicKey, setRewardSharePublicKey] = useState(""); const [rewardSharePublicKey, setRewardSharePublicKey] = useState('');
const [mintingKey, setMintingKey] = useState(""); const [mintingKey, setMintingKey] = useState('');
const [rewardsharekey, setRewardsharekey] = useState(""); const [rewardsharekey, setRewardsharekey] = useState('');
const [rewardShares, setRewardShares] = useState([]); const [rewardShares, setRewardShares] = useState([]);
const [nodeInfos, setNodeInfos] = useState({}); const [nodeInfos, setNodeInfos] = useState({});
const [openSnack, setOpenSnack] = useState(false); const [openSnack, setOpenSnack] = useState(false);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { show: showKey, message } = useModal(); const { show: showKey, message } = useModal();
const { isShow: isShowNext, onOk, show: showNext } = useModal(); const { isShow: isShowNext, onOk, show: showNext } = useModal();
const [info, setInfo] = useState(null); const [info, setInfo] = useState(null);
const [names, setNames] = useState({}); const [names, setNames] = useState({});
const [accountInfos, setAccountInfos] = useState({}); const [accountInfos, setAccountInfos] = useState({});
const [showWaitDialog, setShowWaitDialog] = useState(false) const [showWaitDialog, setShowWaitDialog] = useState(false);
const isPartOfMintingGroup = useMemo(() => { const isPartOfMintingGroup = useMemo(() => {
if (groups?.length === 0) return false; if (groups?.length === 0) return false;
return !!groups?.find((item) => item?.groupId?.toString() === "694"); return !!groups?.find((item) => item?.groupId?.toString() === '694');
}, [groups]); }, [groups]);
const getMintingAccounts = useCallback(async () => { const getMintingAccounts = useCallback(async () => {
try { try {
const url = `${getBaseApiReact()}/admin/mintingaccounts`; const url = `${getBaseApiReact()}/admin/mintingaccounts`;
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) { if (!response.ok) {
throw new Error("network error"); throw new Error('network error');
} }
const data = await response.json(); const data = await response.json();
setMintingAccounts(data); setMintingAccounts(data);
@ -117,7 +117,7 @@ export const Minting = ({
const url = `${getBaseApiReact()}/addresses/${address}`; const url = `${getBaseApiReact()}/addresses/${address}`;
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) { if (!response.ok) {
throw new Error("network error"); throw new Error('network error');
} }
const data = await response.json(); const data = await response.json();
if (others) { if (others) {
@ -144,10 +144,10 @@ export const Minting = ({
}; };
useEffect(() => { useEffect(() => {
subscribeToEvent("refresh-rewardshare-list", refreshRewardShare); subscribeToEvent('refresh-rewardshare-list', refreshRewardShare);
return () => { return () => {
unsubscribeFromEvent("refresh-rewardshare-list", refreshRewardShare); unsubscribeFromEvent('refresh-rewardshare-list', refreshRewardShare);
}; };
}, [myAddress]); }, [myAddress]);
@ -177,15 +177,15 @@ export const Minting = ({
try { try {
const url = `${getBaseApiReact()}/admin/status`; const url = `${getBaseApiReact()}/admin/status`;
const response = await fetch(url, { const response = await fetch(url, {
method: "GET", method: 'GET',
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
}, },
}); });
const data = await response.json(); const data = await response.json();
setNodeInfos(data); setNodeInfos(data);
} catch (error) { } 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 url = `${getBaseApiReact()}/addresses/rewardshares?involving=${address}`;
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) { if (!response.ok) {
throw new Error("network error"); throw new Error('network error');
} }
const data = await response.json(); const data = await response.json();
setRewardShares(data); setRewardShares(data);
return data return data;
} catch (error) {} } catch (error) {}
}, []); }, []);
@ -208,10 +208,10 @@ export const Minting = ({
return await new Promise((res, rej) => { return await new Promise((res, rej) => {
window window
.sendMessage( .sendMessage(
"ADMIN_ACTION", 'ADMIN_ACTION',
{ {
type: "addmintingaccount", type: 'addmintingaccount',
value: val, value: val,
}, },
180000, 180000,
@ -220,7 +220,7 @@ export const Minting = ({
.then((response) => { .then((response) => {
if (!response?.error) { if (!response?.error) {
res(response); res(response);
setMintingKey(""); setMintingKey('');
setTimeout(() => { setTimeout(() => {
getMintingAccounts(); getMintingAccounts();
}, 300); }, 300);
@ -229,13 +229,13 @@ export const Minting = ({
rej({ message: response.error }); rej({ message: response.error });
}) })
.catch((error) => { .catch((error) => {
rej({ message: error.message || "An error occurred" }); rej({ message: error.message || 'An error occurred' });
}); });
}); });
} catch (error) { } catch (error) {
setInfo({ setInfo({
type: "error", type: 'error',
message: error?.message || "Unable to add minting account", message: error?.message || 'Unable to add minting account',
}); });
setOpenSnack(true); setOpenSnack(true);
} finally { } finally {
@ -249,10 +249,10 @@ export const Minting = ({
return await new Promise((res, rej) => { return await new Promise((res, rej) => {
window window
.sendMessage( .sendMessage(
"ADMIN_ACTION", 'ADMIN_ACTION',
{ {
type: "removemintingaccount", type: 'removemintingaccount',
value: val, value: val,
}, },
180000, 180000,
@ -270,13 +270,13 @@ export const Minting = ({
rej({ message: response.error }); rej({ message: response.error });
}) })
.catch((error) => { .catch((error) => {
rej({ message: error.message || "An error occurred" }); rej({ message: error.message || 'An error occurred' });
}); });
}); });
} catch (error) { } catch (error) {
setInfo({ setInfo({
type: "error", type: 'error',
message: error?.message || "Unable to remove minting account", message: error?.message || 'Unable to remove minting account',
}); });
setOpenSnack(true); setOpenSnack(true);
} finally { } finally {
@ -285,14 +285,14 @@ export const Minting = ({
}, []); }, []);
const createRewardShare = useCallback(async (publicKey, recipient) => { const createRewardShare = useCallback(async (publicKey, recipient) => {
const fee = await getFee("REWARD_SHARE"); const fee = await getFee('REWARD_SHARE');
await show({ await show({
message: "Would you like to perform an REWARD_SHARE transaction?", message: 'Would you like to perform an REWARD_SHARE transaction?',
publishFee: fee.fee + " QORT", publishFee: fee.fee + ' QORT',
}); });
return await new Promise((res, rej) => { return await new Promise((res, rej) => {
window window
.sendMessage("createRewardShare", { .sendMessage('createRewardShare', {
recipientPublicKey: publicKey, recipientPublicKey: publicKey,
}) })
.then((response) => { .then((response) => {
@ -301,7 +301,7 @@ export const Minting = ({
{ {
recipient, recipient,
...response, ...response,
type: "add-rewardShare", type: 'add-rewardShare',
label: `Add rewardshare: awaiting confirmation`, label: `Add rewardshare: awaiting confirmation`,
labelDone: `Add rewardshare: success!`, labelDone: `Add rewardshare: success!`,
done: false, done: false,
@ -314,7 +314,7 @@ export const Minting = ({
rej({ message: response.error }); rej({ message: response.error });
}) })
.catch((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) => { const getRewardSharePrivateKey = useCallback(async (publicKey) => {
return await new Promise((res, rej) => { return await new Promise((res, rej) => {
window window
.sendMessage("getRewardSharePrivateKey", { .sendMessage('getRewardSharePrivateKey', {
recipientPublicKey: publicKey, recipientPublicKey: publicKey,
}) })
.then((response) => { .then((response) => {
@ -333,7 +333,7 @@ export const Minting = ({
rej({ message: response.error }); rej({ message: response.error });
}) })
.catch((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 waitUntilRewardShareIsConfirmed = async (timeoutMs = 600000) => {
const pollingInterval = 30000; const pollingInterval = 30000;
const startTime = Date.now(); const startTime = Date.now();
const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); const sleep = (ms) => new Promise((res) => setTimeout(res, ms));
while (Date.now() - startTime < timeoutMs) { while (Date.now() - startTime < timeoutMs) {
const rewardShares = await getRewardShares(myAddress);
const rewardShares = await getRewardShares(myAddress); const findRewardShare = rewardShares?.find(
const findRewardShare = rewardShares?.find( (item) =>
(item) => item?.recipient === myAddress && item?.mintingAccount === myAddress
item?.recipient === myAddress && item?.mintingAccount === myAddress );
);
if (findRewardShare) {
if (findRewardShare) { return true; // Exit early if found
return true; // Exit early if found }
}
await sleep(pollingInterval); // Wait before the next poll 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 () => { const startMinting = async () => {
@ -377,23 +375,22 @@ export const Minting = ({
addMintingAccount(privateRewardShare); addMintingAccount(privateRewardShare);
} else { } else {
await createRewardShare(accountInfo?.publicKey, myAddress); await createRewardShare(accountInfo?.publicKey, myAddress);
setShowWaitDialog(true) setShowWaitDialog(true);
await waitUntilRewardShareIsConfirmed() await waitUntilRewardShareIsConfirmed();
await showNext({ await showNext({
message: '' message: '',
}) });
const privateRewardShare = await getRewardSharePrivateKey( const privateRewardShare = await getRewardSharePrivateKey(
accountInfo?.publicKey accountInfo?.publicKey
); );
setShowWaitDialog(false) setShowWaitDialog(false);
addMintingAccount(privateRewardShare); addMintingAccount(privateRewardShare);
} }
} catch (error) { } catch (error) {
setShowWaitDialog(false) setShowWaitDialog(false);
setInfo({ setInfo({
type: "error", type: 'error',
message: error?.message || "Unable to start minting", message: error?.message || 'Unable to start minting',
}); });
setOpenSnack(true); setOpenSnack(true);
} finally { } finally {
@ -412,13 +409,13 @@ export const Minting = ({
const url = `${getBaseApiReact()}/groups/member/${address}`; const url = `${getBaseApiReact()}/groups/member/${address}`;
const response = await fetch(url); const response = await fetch(url);
const data = await response.json(); 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) => { const removeRewardShare = useCallback(async (rewardShare) => {
return await new Promise((res, rej) => { return await new Promise((res, rej) => {
window window
.sendMessage("removeRewardShare", { .sendMessage('removeRewardShare', {
rewardShareKeyPairPublicKey: rewardShare.rewardSharePublicKey, rewardShareKeyPairPublicKey: rewardShare.rewardSharePublicKey,
recipient: rewardShare.recipient, recipient: rewardShare.recipient,
percentageShare: -1, percentageShare: -1,
@ -430,7 +427,7 @@ export const Minting = ({
{ {
...rewardShare, ...rewardShare,
...response, ...response,
type: "remove-rewardShare", type: 'remove-rewardShare',
label: `Remove rewardshare: awaiting confirmation`, label: `Remove rewardshare: awaiting confirmation`,
labelDone: `Remove rewardshare: success!`, labelDone: `Remove rewardshare: success!`,
done: false, done: false,
@ -442,7 +439,7 @@ export const Minting = ({
rej({ message: response.error }); rej({ message: response.error });
}) })
.catch((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); const privateRewardShare = await removeRewardShare(rewardShare);
} catch (error) { } catch (error) {
setInfo({ setInfo({
type: "error", type: 'error',
message: error?.message || "Unable to remove reward share", message: error?.message || 'Unable to remove reward share',
}); });
setOpenSnack(true); setOpenSnack(true);
} finally { } finally {
@ -468,9 +465,9 @@ export const Minting = ({
setIsLoading(true); setIsLoading(true);
const confirmReceiver = await getNameOrAddress(receiver); const confirmReceiver = await getNameOrAddress(receiver);
if (confirmReceiver.error) if (confirmReceiver.error)
throw new Error("Invalid receiver address or name"); throw new Error('Invalid receiver address or name');
const isInMinterGroup = await checkIfMinterGroup(confirmReceiver); 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 publicKey = await getPublicKeyFromAddress(confirmReceiver);
const findRewardShare = rewardShares?.find( const findRewardShare = rewardShares?.find(
(item) => (item) =>
@ -487,8 +484,8 @@ export const Minting = ({
} }
} catch (error) { } catch (error) {
setInfo({ setInfo({
type: "error", type: 'error',
message: error?.message || "Unable to create reward share", message: error?.message || 'Unable to create reward share',
}); });
setOpenSnack(true); setOpenSnack(true);
} finally { } finally {
@ -550,11 +547,9 @@ export const Minting = ({
(accountInfo?.blocksMinted + accountInfo?.blocksMintedAdjustment); (accountInfo?.blocksMinted + accountInfo?.blocksMintedAdjustment);
let countBlocksString = countBlocks.toString(); let countBlocksString = countBlocks.toString();
return "" + countBlocksString; return '' + countBlocksString;
}; };
return ( return (
<Dialog <Dialog
open={true} open={true}
@ -562,19 +557,19 @@ export const Minting = ({
fullWidth fullWidth
fullScreen fullScreen
sx={{ sx={{
"& .MuiDialog-paper": { '& .MuiDialog-paper': {
margin: 0, margin: 0,
maxWidth: "100%", maxWidth: '100%',
width: "100%", width: '100%',
height: "100vh", height: '100vh',
overflow: "hidden", // Prevent scrollbars overflow: 'hidden', // Prevent scrollbars
}, },
}} }}
> >
<DialogTitle id="alert-dialog-title">{"Manage your minting"}</DialogTitle> <DialogTitle id="alert-dialog-title">{'Manage your minting'}</DialogTitle>
<IconButton <IconButton
sx={{ sx={{
position: "absolute", position: 'absolute',
right: 8, right: 8,
top: 8, top: 8,
}} }}
@ -586,20 +581,20 @@ export const Minting = ({
</IconButton> </IconButton>
<DialogContent <DialogContent
sx={{ sx={{
position: "relative", position: 'relative',
}} }}
> >
{isLoading && ( {isLoading && (
<Box <Box
sx={{ sx={{
position: "absolute", position: 'absolute',
top: 0, top: 0,
left: 0, left: 0,
right: 0, right: 0,
bottom: 0, bottom: 0,
display: "flex", display: 'flex',
justifyContent: "center", justifyContent: 'center',
alignItems: "center", alignItems: 'center',
}} }}
> >
<FidgetSpinner <FidgetSpinner
@ -614,8 +609,8 @@ export const Minting = ({
)} )}
<Card <Card
sx={{ sx={{
backgroundColor: "var(--bg-2)", backgroundColor: 'var(--bg-2)',
padding: "10px", padding: '10px',
}} }}
> >
<Typography>Account: {handleNames(accountInfo?.address)}</Typography> <Typography>Account: {handleNames(accountInfo?.address)}</Typography>
@ -631,11 +626,11 @@ export const Minting = ({
{isPartOfMintingGroup && !accountIsMinting && ( {isPartOfMintingGroup && !accountIsMinting && (
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
gap: "5px", gap: '5px',
flexDirection: "column", flexDirection: 'column',
width: "100%", width: '100%',
alignItems: "center", alignItems: 'center',
}} }}
> >
<Button <Button
@ -645,15 +640,15 @@ export const Minting = ({
}} }}
disabled={mintingAccounts?.length > 1} disabled={mintingAccounts?.length > 1}
sx={{ sx={{
backgroundColor: "var(--green)", backgroundColor: 'var(--green)',
color: "black", color: 'black',
fontWeight: "bold", fontWeight: 'bold',
opacity: 0.7, opacity: 0.7,
maxWidth: "90%", maxWidth: '90%',
width: "200px", width: '200px',
"&:hover": { '&:hover': {
backgroundColor: "var(--green)", backgroundColor: 'var(--green)',
color: "black", color: 'black',
opacity: 1, opacity: 1,
}, },
}} }}
@ -675,16 +670,16 @@ export const Minting = ({
)} )}
<Card <Card
sx={{ sx={{
backgroundColor: "var(--bg-2)", backgroundColor: 'var(--bg-2)',
padding: "10px", padding: '10px',
}} }}
> >
{accountIsMinting && ( {accountIsMinting && (
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
gap: "5px", gap: '5px',
flexDirection: "column", flexDirection: 'column',
}} }}
> >
<Typography> <Typography>
@ -698,9 +693,9 @@ export const Minting = ({
<Box <Box
key={acct?.mintingAccount} key={acct?.mintingAccount}
sx={{ sx={{
display: "flex", display: 'flex',
gap: "10px", gap: '10px',
flexDirection: "column", flexDirection: 'column',
}} }}
> >
<Typography> <Typography>
@ -709,15 +704,15 @@ export const Minting = ({
<Button <Button
size="small" size="small"
sx={{ sx={{
backgroundColor: "var(--danger)", backgroundColor: 'var(--danger)',
color: "black", color: 'black',
fontWeight: "bold", fontWeight: 'bold',
opacity: 0.7, opacity: 0.7,
maxWidth: "90%", maxWidth: '90%',
width: "200px", width: '200px',
"&:hover": { '&:hover': {
backgroundColor: "var(--danger)", backgroundColor: 'var(--danger)',
color: "black", color: 'black',
opacity: 1, opacity: 1,
}, },
}} }}
@ -745,17 +740,17 @@ export const Minting = ({
{!isPartOfMintingGroup && ( {!isPartOfMintingGroup && (
<Card <Card
sx={{ sx={{
backgroundColor: "var(--bg-2)", backgroundColor: 'var(--bg-2)',
padding: "10px", padding: '10px',
}} }}
> >
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
gap: "5px", gap: '5px',
flexDirection: "column", flexDirection: 'column',
width: "100%", width: '100%',
alignItems: "center", alignItems: 'center',
}} }}
> >
<Typography> <Typography>
@ -768,22 +763,22 @@ export const Minting = ({
<Button <Button
size="small" size="small"
sx={{ sx={{
backgroundColor: "var(--green)", backgroundColor: 'var(--green)',
color: "black", color: 'black',
fontWeight: "bold", fontWeight: 'bold',
opacity: 0.7, opacity: 0.7,
"&:hover": { '&:hover': {
backgroundColor: "var(--green)", backgroundColor: 'var(--green)',
color: "black", color: 'black',
opacity: 1, opacity: 1,
}, },
}} }}
onClick={() => { onClick={() => {
executeEvent("addTab", { executeEvent('addTab', {
data: { service: "APP", name: "q-mintership" }, data: { service: 'APP', name: 'q-mintership' },
}); });
executeEvent("open-apps-mode", {}); executeEvent('open-apps-mode', {});
setIsOpenMinting(false); setIsOpenMinting(false);
}} }}
variant="contained" variant="contained"
@ -801,29 +796,32 @@ export const Minting = ({
aria-describedby="alert-dialog-description" aria-describedby="alert-dialog-description"
> >
<DialogTitle id="alert-dialog-title"> <DialogTitle id="alert-dialog-title">
{isShowNext ? "Confirmed" : "Please Wait"} {isShowNext ? 'Confirmed' : 'Please Wait'}
</DialogTitle> </DialogTitle>
<DialogContent> <DialogContent>
{!isShowNext && ( {!isShowNext && (
<Typography> <Typography>
Confirming creation of rewardshare on chain. Please be patient, this could take up to 90 seconds. Confirming creation of rewardshare on chain. Please be
</Typography> patient, this could take up to 90 seconds.
</Typography>
)} )}
{isShowNext && ( {isShowNext && (
<Typography> <Typography>
Rewardshare confirmed. Please click Next. Rewardshare confirmed. Please click Next.
</Typography> </Typography>
)} )}
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button disabled={!isShowNext} variant="contained" onClick={onOk} autoFocus> <Button
disabled={!isShowNext}
variant="contained"
onClick={onOk}
autoFocus
>
Next Next
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
)} )}
</DialogContent> </DialogContent>
@ -837,7 +835,7 @@ export const Minting = ({
</Button> </Button>
</DialogActions> </DialogActions>
<Snackbar <Snackbar
anchorOrigin={{ vertical: "bottom", horizontal: "center" }} anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
open={openSnack} open={openSnack}
autoHideDuration={6000} autoHideDuration={6000}
onClose={handleClose} onClose={handleClose}
@ -846,7 +844,7 @@ export const Minting = ({
onClose={handleClose} onClose={handleClose}
severity={info?.type} severity={info?.type}
variant="filled" variant="filled"
sx={{ width: "100%" }} sx={{ width: '100%' }}
> >
{info?.message} {info?.message}
</Alert> </Alert>

View File

@ -1,167 +1,170 @@
import { Box, CircularProgress } from '@mui/material'; import { Box, CircularProgress } from '@mui/material';
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react';
import { CustomButton, CustomInput, CustomLabel, TextP } from '../App-styles'; import {
CustomButton,
CustomInput,
CustomLabel,
TextP,
} from '../styles/App-styles';
import { Spacer } from '../common/Spacer'; import { Spacer } from '../common/Spacer';
import BoundedNumericTextField from '../common/BoundedNumericTextField'; import BoundedNumericTextField from '../common/BoundedNumericTextField';
import { PasswordField } from './PasswordField/PasswordField'; import { PasswordField } from './PasswordField/PasswordField';
import { ErrorText } from './ErrorText/ErrorText'; import { ErrorText } from './ErrorText/ErrorText';
import { getFee } from '../background'; import { getFee } from '../background';
export const QortPayment = ({balance, show, onSuccess, defaultPaymentTo}) => { export const QortPayment = ({ balance, show, onSuccess, defaultPaymentTo }) => {
const [paymentTo, setPaymentTo] = useState<string>(defaultPaymentTo); const [paymentTo, setPaymentTo] = useState<string>(defaultPaymentTo);
const [paymentAmount, setPaymentAmount] = useState<number>(0); const [paymentAmount, setPaymentAmount] = useState<number>(0);
const [paymentPassword, setPaymentPassword] = useState<string>(""); const [paymentPassword, setPaymentPassword] = useState<string>('');
const [sendPaymentError, setSendPaymentError] = useState<string>(""); const [sendPaymentError, setSendPaymentError] = useState<string>('');
const [sendPaymentSuccess, setSendPaymentSuccess] = useState<string>(""); const [sendPaymentSuccess, setSendPaymentSuccess] = useState<string>('');
const [isLoadingSendCoin, setIsLoadingSendCoin] = useState<boolean>(false); 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');
await show({
const sendCoinFunc = async() => { message: `Would you like to transfer ${Number(paymentAmount)} QORT?`,
try { paymentFee: fee.fee + ' QORT',
setSendPaymentError(""); });
setSendPaymentSuccess(""); setIsLoadingSendCoin(true);
if (!paymentTo) { window
setSendPaymentError("Please enter a recipient"); .sendMessage('sendCoin', {
return; amount: Number(paymentAmount),
receiver: paymentTo.trim(),
password: paymentPassword,
})
.then((response) => {
if (response?.error) {
setSendPaymentError(response.error);
} else {
onSuccess();
} }
if (!paymentAmount) { setIsLoadingSendCoin(false);
setSendPaymentError("Please enter an amount greater than 0"); })
return; .catch((error) => {
} console.error('Failed to send coin:', error);
if (!paymentPassword) { setIsLoadingSendCoin(false);
setSendPaymentError("Please enter your wallet password"); });
return; } catch (error) {
} // error
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
}
};
return ( return (
<> <>
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
alignItems: "flex-start", alignItems: 'flex-start',
}} }}
> >
<TextP <TextP
sx={{ sx={{
textAlign: "start", textAlign: 'start',
lineHeight: "24px", lineHeight: '24px',
fontSize: "20px", fontSize: '20px',
fontWeight: 600, fontWeight: 600,
}} }}
> >
Transfer QORT Transfer QORT
</TextP> </TextP>
<Spacer height="35px" /> <Spacer height="35px" />
<TextP <TextP
sx={{ sx={{
textAlign: "start", textAlign: 'start',
lineHeight: "16px", lineHeight: '16px',
fontSize: "20px", fontSize: '20px',
fontWeight: 600, fontWeight: 600,
color: "rgba(255, 255, 255, 0.5)", color: 'rgba(255, 255, 255, 0.5)',
}} }}
> >
Balance: Balance:
</TextP> </TextP>
<TextP <TextP
sx={{ sx={{
textAlign: "start", textAlign: 'start',
lineHeight: "24px", lineHeight: '24px',
fontSize: "20px", fontSize: '20px',
fontWeight: 700, fontWeight: 700,
}} }}
> >
{balance?.toFixed(2)} QORT {balance?.toFixed(2)} QORT
</TextP> </TextP>
</Box> </Box>
<Spacer height="35px" /> <Spacer height="35px" />
<Box> <Box>
<CustomLabel htmlFor="standard-adornment-name">To</CustomLabel> <CustomLabel htmlFor="standard-adornment-name">To</CustomLabel>
<Spacer height="5px" /> <Spacer height="5px" />
<CustomInput <CustomInput
id="standard-adornment-name" id="standard-adornment-name"
value={paymentTo} value={paymentTo}
onChange={(e) => setPaymentTo(e.target.value)} onChange={(e) => setPaymentTo(e.target.value)}
autoComplete="off" autoComplete="off"
/> />
<Spacer height="6px" /> <Spacer height="6px" />
<CustomLabel htmlFor="standard-adornment-amount"> <CustomLabel htmlFor="standard-adornment-amount">Amount</CustomLabel>
Amount <Spacer height="5px" />
</CustomLabel> <BoundedNumericTextField
<Spacer height="5px" /> value={paymentAmount}
<BoundedNumericTextField minValue={0}
value={paymentAmount} maxValue={+balance}
minValue={0} allowDecimals={true}
maxValue={+balance} initialValue={'0'}
allowDecimals={true} allowNegatives={false}
initialValue={'0'} afterChange={(e: string) => setPaymentAmount(+e)}
allowNegatives={false} />
afterChange={(e: string) => setPaymentAmount(+e)} <Spacer height="6px" />
/> <CustomLabel htmlFor="standard-adornment-password">
<Spacer height="6px" /> Confirm Wallet Password
<CustomLabel htmlFor="standard-adornment-password"> </CustomLabel>
Confirm Wallet Password <Spacer height="5px" />
</CustomLabel> <PasswordField
<Spacer height="5px" /> id="standard-adornment-password"
<PasswordField value={paymentPassword}
id="standard-adornment-password" onChange={(e) => setPaymentPassword(e.target.value)}
value={paymentPassword} autoComplete="off"
onChange={(e) => setPaymentPassword(e.target.value)} />
autoComplete="off" </Box>
/> <Spacer height="10px" />
</Box> <ErrorText>{sendPaymentError}</ErrorText>
<Spacer height="10px" /> {/* <Typography>{sendPaymentSuccess}</Typography> */}
<ErrorText>{sendPaymentError}</ErrorText> <Spacer height="25px" />
{/* <Typography>{sendPaymentSuccess}</Typography> */} <CustomButton
<Spacer height="25px" /> sx={{
<CustomButton cursor: isLoadingSendCoin ? 'default' : 'pointer',
}}
onClick={() => {
if (isLoadingSendCoin) return;
sendCoinFunc();
}}
>
{isLoadingSendCoin && (
<CircularProgress
size={16}
sx={{ sx={{
cursor: isLoadingSendCoin ? 'default' : 'pointer' color: 'white',
}} }}
onClick={() => { />
if(isLoadingSendCoin) return )}
sendCoinFunc(); Send
}} </CustomButton>
>
{isLoadingSendCoin && (
<CircularProgress size={16} sx={{
color: 'white'
}} />
)}
Send
</CustomButton>
</> </>
) );
} };

View File

@ -0,0 +1,29 @@
import { createContext, useContext, useState, useMemo } from 'react';
import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles';
import { darkTheme, lightTheme } from '../../styles/theme';
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 toggleTheme = () => {
setThemeMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light'));
};
return (
<ThemeContext.Provider value={{ themeMode, toggleTheme }}>
<MuiThemeProvider theme={theme}>{children}</MuiThemeProvider>
</ThemeContext.Provider>
);
};
export const useThemeContext = () => useContext(ThemeContext);

View File

@ -0,0 +1,77 @@
import { useThemeContext } from "./ThemeContext";
import { styled, Switch } from "@mui/material";
const ThemeSwitch = styled(Switch)(({ theme }) => ({
width: 62,
height: 34,
padding: 7,
"& .MuiSwitch-switchBase": {
margin: 1,
padding: 0,
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"
)}" 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": {
opacity: 1,
backgroundColor: "#aab4be",
...theme.applyStyles("dark", {
backgroundColor: "#8796A5",
}),
},
},
},
"& .MuiSwitch-thumb": {
backgroundColor: "#001e3c",
width: 32,
height: 32,
"&::before": {
content: "''",
position: "absolute",
width: "100%",
height: "100%",
left: 0,
top: 0,
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"
)}" 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",
}),
},
"& .MuiSwitch-track": {
opacity: 1,
backgroundColor: "#aab4be",
borderRadius: 20 / 2,
...theme.applyStyles("dark", {
backgroundColor: "#8796A5",
}),
},
}));
const ThemeSelector = ({ style }) => {
const { themeMode, toggleTheme } = useThemeContext();
return (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "1px",
...style,
}}
>
<ThemeSwitch checked={themeMode === "dark"} onChange={toggleTheme} />
</div>
);
};
export default ThemeSelector;