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,16 +37,16 @@ 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;
@ -41,10 +55,10 @@ export const AnnouncementDiscussion = ({
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,13 +66,15 @@ 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) => {
@ -67,7 +83,6 @@ export const AnnouncementDiscussion = ({
[`${identifier}-${name}`]: messageData, [`${identifier}-${name}`]: messageData,
}; };
}); });
} catch (error) {} } catch (error) {}
}; };
@ -76,7 +91,8 @@ export const AnnouncementDiscussion = ({
if (!selectedAnnouncement) return; if (!selectedAnnouncement) return;
return new Promise((res, rej) => { return new Promise((res, rej) => {
window.sendMessage("publishGroupEncryptedResource", { window
.sendMessage('publishGroupEncryptedResource', {
encryptedData, encryptedData,
identifier, identifier,
}) })
@ -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) setTempPublishedList(tempData);
}
} catch (error) {
}
} }
} 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 =
isPrivate === false ? null : await getSecretKey(false, true);
const message64: any = await objectToBase64(message); const message64: any = await objectToBase64(message);
const encryptSingle = isPrivate === false ? message64 : await encryptChatMessage( const encryptSingle =
message64, isPrivate === false
secretKeyObject ? 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,10 +166,10 @@ 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();
} }
@ -164,7 +177,7 @@ export const AnnouncementDiscussion = ({
} 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,31 +215,28 @@ 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
@ -235,87 +244,93 @@ export const AnnouncementDiscussion = ({
// 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',
flexDirection: 'column',
flexShrink: 0, flexShrink: 0,
}}> }}
>
<AuthenticatedContainerInnerTop sx={{ <AuthenticatedContainerInnerTop
height: '20px' sx={{
}}> height: '20px',
<ArrowBackIcon onClick={()=> setSelectedAnnouncement(null)} sx={{ }}
cursor: 'pointer' >
}} /> <ArrowBackIcon
onClick={() => setSelectedAnnouncement(null)}
sx={{
cursor: 'pointer',
}}
/>
</AuthenticatedContainerInnerTop> </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,24 +338,26 @@ 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
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 && (
<CustomButton <CustomButton
onClick={()=> { onClick={() => {
if(isSending) return if (isSending) return;
setIsFocusedParent(false) setIsFocusedParent(false);
clearEditorContent() clearEditorContent();
// Unfocus the editor // Unfocus the editor
}} }}
style={{ style={{
@ -353,10 +370,8 @@ export const AnnouncementDiscussion = ({
background: 'red', background: 'red',
}} }}
> >
{` Close`} {` Close`}
</CustomButton> </CustomButton>
)} )}
<CustomButton <CustomButton
onClick={() => { onClick={() => {
@ -364,38 +379,37 @@ export const AnnouncementDiscussion = ({
publishComment(); publishComment();
}} }}
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',
}} }}
/> />
)} )}
{` Publish Comment`} {` Publish Comment`}
</CustomButton> </CustomButton>
</Box> </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 myName={myName} disableComment={disableComment} setSelectedAnnouncement={setSelectedAnnouncement} message={message} messageData={messageData} /> <AnnouncementItem
myName={myName}
disableComment={disableComment}
setSelectedAnnouncement={setSelectedAnnouncement}
message={message}
messageData={messageData}
/>
</div> </div>
); );
})} })}
{/* <AutoSizer> {/* <AutoSizer>
@ -83,14 +87,18 @@ export const AnnouncementList = ({
/> />
)} )}
</AutoSizer> */} </AutoSizer> */}
<Box sx={{ <Box
sx={{
width: '100%', width: '100%',
marginTop: '25px', marginTop: '25px',
display: 'flex', display: 'flex',
justifyContent: 'center' justifyContent: 'center',
}}> }}
>
{showLoadMore && ( {showLoadMore && (
<CustomButton onClick={loadMore}>Load older announcements</CustomButton> <CustomButton onClick={loadMore}>
Load older announcements
</CustomButton>
)} )}
</Box> </Box>
</div> </div>

View File

@ -1,52 +1,78 @@
import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react' import React, {
useCallback,
useEffect,
useMemo,
useReducer,
useRef,
useState,
} from 'react';
import { objectToBase64 } from '../../qdn/encryption/group-encryption' import { objectToBase64 } from '../../qdn/encryption/group-encryption';
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 { CustomButton } from '../../App-styles' import { CustomButton } from '../../styles/App-styles';
import CircularProgress from '@mui/material/CircularProgress'; import CircularProgress from '@mui/material/CircularProgress';
import { Box, ButtonBase, Input, Typography } from '@mui/material'; import { Box, ButtonBase, Input, Typography } from '@mui/material';
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
import { getNameInfo } from '../Group/Group'; import { getNameInfo } from '../Group/Group';
import { Spacer } from '../../common/Spacer'; import { Spacer } from '../../common/Spacer';
import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { CustomizedSnackbars } from '../Snackbar/Snackbar';
import { getBaseApiReact, getBaseApiReactSocket, isMobile, pauseAllQueues, resumeAllQueues } from '../../App'; import {
getBaseApiReact,
getBaseApiReactSocket,
isMobile,
pauseAllQueues,
resumeAllQueues,
} from '../../App';
import { getPublicKey } from '../../background'; import { getPublicKey } from '../../background';
import { useMessageQueue } from '../../MessageQueueContext'; import { useMessageQueue } from '../../MessageQueueContext';
import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import {
executeEvent,
subscribeToEvent,
unsubscribeFromEvent,
} from '../../utils/events';
import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import ShortUniqueId from "short-unique-id"; import ShortUniqueId from 'short-unique-id';
import { ReturnIcon } from '../../assets/Icons/ReturnIcon'; import { ReturnIcon } from '../../assets/Icons/ReturnIcon';
import { ExitIcon } from '../../assets/Icons/ExitIcon'; import { ExitIcon } from '../../assets/Icons/ExitIcon';
import { MessageItem, ReplyPreview } from './MessageItem'; import { MessageItem, ReplyPreview } from './MessageItem';
const uid = new ShortUniqueId({ length: 5 }); const uid = new ShortUniqueId({ length: 5 });
export const ChatDirect = ({
export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDirect, setNewChat, getTimestampEnterChat, myName, balance, close, setMobileViewModeKeepOpen}) => { myAddress,
const { queueChats, addToQueue, processWithNewMessages} = useMessageQueue(); isNewChat,
selectedDirect,
setSelectedDirect,
setNewChat,
getTimestampEnterChat,
myName,
balance,
close,
setMobileViewModeKeepOpen,
}) => {
const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue();
const [isFocusedParent, setIsFocusedParent] = useState(false); const [isFocusedParent, setIsFocusedParent] = useState(false);
const [onEditMessage, setOnEditMessage] = useState(null) const [onEditMessage, setOnEditMessage] = useState(null);
const [messages, setMessages] = useState([]) const [messages, setMessages] = useState([]);
const [isSending, setIsSending] = useState(false) const [isSending, setIsSending] = useState(false);
const [directToValue, setDirectToValue] = useState('') const [directToValue, setDirectToValue] = useState('');
const hasInitialized = useRef(false) const hasInitialized = useRef(false);
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false);
const [openSnack, setOpenSnack] = React.useState(false); const [openSnack, setOpenSnack] = React.useState(false);
const [infoSnack, setInfoSnack] = React.useState(null); const [infoSnack, setInfoSnack] = React.useState(null);
const [publicKeyOfRecipient, setPublicKeyOfRecipient] = React.useState("") const [publicKeyOfRecipient, setPublicKeyOfRecipient] = React.useState('');
const hasInitializedWebsocket = useRef(false) const hasInitializedWebsocket = useRef(false);
const [chatReferences, setChatReferences] = useState({}) const [chatReferences, setChatReferences] = useState({});
const editorRef = useRef(null); const editorRef = useRef(null);
const socketRef = useRef(null); const socketRef = useRef(null);
const timeoutIdRef = useRef(null); const timeoutIdRef = useRef(null);
const [messageSize, setMessageSize] = useState(0) const [messageSize, setMessageSize] = useState(0);
const groupSocketTimeoutRef = useRef(null); const groupSocketTimeoutRef = useRef(null);
const [replyMessage, setReplyMessage] = useState(null) const [replyMessage, setReplyMessage] = useState(null);
const setEditorRef = (editorInstance) => { const setEditorRef = (editorInstance) => {
editorRef.current = editorInstance; editorRef.current = editorInstance;
}; };
@ -55,42 +81,47 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi
const triggerRerender = () => { const triggerRerender = () => {
forceUpdate(); // Trigger re-render by updating the state forceUpdate(); // Trigger re-render by updating the state
}; };
const publicKeyOfRecipientRef = useRef(null) const publicKeyOfRecipientRef = useRef(null);
const getPublicKeyFunc = async (address)=> { const getPublicKeyFunc = async (address) => {
try { try {
const publicKey = await getPublicKey(address) const publicKey = await getPublicKey(address);
if(publicKeyOfRecipientRef.current !== selectedDirect?.address) return if (publicKeyOfRecipientRef.current !== selectedDirect?.address) return;
setPublicKeyOfRecipient(publicKey) setPublicKeyOfRecipient(publicKey);
} catch (error) { } catch (error) {}
};
const tempMessages = useMemo(() => {
if (!selectedDirect?.address) return [];
if (queueChats[selectedDirect?.address]) {
return queueChats[selectedDirect?.address]?.filter(
(item) => !item?.chatReference
);
} }
return [];
}, [selectedDirect?.address, queueChats]);
const tempChatReferences = useMemo(() => {
if (!selectedDirect?.address) return [];
if (queueChats[selectedDirect?.address]) {
return queueChats[selectedDirect?.address]?.filter(
(item) => !!item?.chatReference
);
} }
return [];
}, [selectedDirect?.address, queueChats]);
const tempMessages = useMemo(()=> { useEffect(() => {
if(!selectedDirect?.address) return [] if (selectedDirect?.address) {
if(queueChats[selectedDirect?.address]){ publicKeyOfRecipientRef.current = selectedDirect?.address;
return queueChats[selectedDirect?.address]?.filter((item)=> !item?.chatReference) getPublicKeyFunc(publicKeyOfRecipientRef.current);
} }
return [] }, [selectedDirect?.address]);
}, [selectedDirect?.address, queueChats])
const tempChatReferences = useMemo(()=> { const middletierFunc = async (
if(!selectedDirect?.address) return [] data: any,
if(queueChats[selectedDirect?.address]){ selectedDirectAddress: string,
return queueChats[selectedDirect?.address]?.filter((item)=> !!item?.chatReference) myAddress: string
} ) => {
return []
}, [selectedDirect?.address, queueChats])
useEffect(()=> {
if(selectedDirect?.address){
publicKeyOfRecipientRef.current = selectedDirect?.address
getPublicKeyFunc(publicKeyOfRecipientRef.current)
}
}, [selectedDirect?.address])
const middletierFunc = async (data: any, selectedDirectAddress: string, myAddress: string) => {
try { try {
if (hasInitialized.current) { if (hasInitialized.current) {
decryptMessages(data, true); decryptMessages(data, true);
@ -99,9 +130,9 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi
hasInitialized.current = true; hasInitialized.current = true;
const url = `${getBaseApiReact()}/chat/messages?involving=${selectedDirectAddress}&involving=${myAddress}&encoding=BASE64&limit=0&reverse=false`; const url = `${getBaseApiReact()}/chat/messages?involving=${selectedDirectAddress}&involving=${myAddress}&encoding=BASE64&limit=0&reverse=false`;
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();
@ -109,22 +140,28 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
} };
const decryptMessages = (encryptedMessages: any[], isInitiated: boolean)=> { const decryptMessages = (encryptedMessages: any[], isInitiated: boolean) => {
try { try {
return new Promise((res, rej)=> { return new Promise((res, rej) => {
window.sendMessage("decryptDirect", { window
.sendMessage('decryptDirect', {
data: encryptedMessages, data: encryptedMessages,
involvingAddress: selectedDirect?.address, involvingAddress: selectedDirect?.address,
}) })
.then((decryptResponse) => { .then((decryptResponse) => {
if (!decryptResponse?.error) { if (!decryptResponse?.error) {
const response = processWithNewMessages(decryptResponse, selectedDirect?.address); const response = processWithNewMessages(
decryptResponse,
selectedDirect?.address
);
res(response); res(response);
if (isInitiated) { if (isInitiated) {
const formatted = response.filter((rawItem) => !rawItem?.chatReference).map((item) => ({ const formatted = response
.filter((rawItem) => !rawItem?.chatReference)
.map((item) => ({
...item, ...item,
id: item.signature, id: item.signature,
text: item.message, text: item.message,
@ -134,21 +171,26 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi
setChatReferences((prev) => { setChatReferences((prev) => {
const organizedChatReferences = { ...prev }; const organizedChatReferences = { ...prev };
response.filter((rawItem) => !!rawItem?.chatReference && rawItem?.type === 'edit').forEach((item) => { response
.filter(
(rawItem) =>
!!rawItem?.chatReference && rawItem?.type === 'edit'
)
.forEach((item) => {
try { try {
organizedChatReferences[item.chatReference] = { organizedChatReferences[item.chatReference] = {
...(organizedChatReferences[item.chatReference] || {}), ...(organizedChatReferences[item.chatReference] ||
edit: item {}),
edit: item,
}; };
} catch(error){ } catch (error) {}
});
} return organizedChatReferences;
}) });
return organizedChatReferences
})
} else { } else {
hasInitialized.current = true; hasInitialized.current = true;
const formatted = response.filter((rawItem) => !rawItem?.chatReference) const formatted = response
.filter((rawItem) => !rawItem?.chatReference)
.map((item) => ({ .map((item) => ({
...item, ...item,
id: item.signature, id: item.signature,
@ -160,32 +202,33 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi
setChatReferences((prev) => { setChatReferences((prev) => {
const organizedChatReferences = { ...prev }; const organizedChatReferences = { ...prev };
response.filter((rawItem) => !!rawItem?.chatReference && rawItem?.type === 'edit').forEach((item) => { response
.filter(
(rawItem) =>
!!rawItem?.chatReference && rawItem?.type === 'edit'
)
.forEach((item) => {
try { try {
organizedChatReferences[item.chatReference] = { organizedChatReferences[item.chatReference] = {
...(organizedChatReferences[item.chatReference] || {}), ...(organizedChatReferences[item.chatReference] ||
edit: item {}),
edit: item,
}; };
} catch(error){ } catch (error) {}
});
} return organizedChatReferences;
}) });
return organizedChatReferences
})
} }
return; return;
} }
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 forceCloseWebSocket = () => { const forceCloseWebSocket = () => {
if (socketRef.current) { if (socketRef.current) {
@ -212,7 +255,6 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi
} }
}; };
const initWebsocketMessageGroup = () => { const initWebsocketMessageGroup = () => {
forceCloseWebSocket(); // Close any existing connection forceCloseWebSocket(); // Close any existing connection
@ -231,7 +273,11 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi
clearTimeout(timeoutIdRef.current); clearTimeout(timeoutIdRef.current);
groupSocketTimeoutRef.current = setTimeout(pingWebSocket, 45000); // Ping every 45 seconds groupSocketTimeoutRef.current = setTimeout(pingWebSocket, 45000); // Ping every 45 seconds
} else { } else {
middletierFunc(JSON.parse(e.data), selectedDirect?.address, myAddress) middletierFunc(
JSON.parse(e.data),
selectedDirect?.address,
myAddress
);
setIsLoading(false); setIsLoading(false);
} }
@ -259,14 +305,14 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi
}; };
}; };
const setDirectChatValueFunc = async (e)=> { const setDirectChatValueFunc = async (e) => {
setDirectToValue(e.detail.directToValue) setDirectToValue(e.detail.directToValue);
} };
useEffect(() => { useEffect(() => {
subscribeToEvent("setDirectToValueNewChat", setDirectChatValueFunc); subscribeToEvent('setDirectToValueNewChat', setDirectChatValueFunc);
return () => { return () => {
unsubscribeFromEvent("setDirectToValueNewChat", setDirectChatValueFunc); unsubscribeFromEvent('setDirectToValueNewChat', setDirectChatValueFunc);
}; };
}, []); }, []);
@ -281,22 +327,30 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi
}; };
}, [selectedDirect?.address, myAddress, isNewChat]); }, [selectedDirect?.address, myAddress, isNewChat]);
const sendChatDirect = async (
{ chatReference = undefined, messageText, otherData }: any,
const sendChatDirect = async ({ chatReference = undefined, messageText, otherData}: any, address, publicKeyOfRecipient, isNewChatVar)=> { address,
publicKeyOfRecipient,
isNewChatVar
) => {
try { try {
const directTo = isNewChatVar ? directToValue : address const directTo = isNewChatVar ? directToValue : address;
if(!directTo) return if (!directTo) return;
return new Promise((res, rej)=> { return new Promise((res, rej) => {
window.sendMessage("sendChatDirect", { window
.sendMessage(
'sendChatDirect',
{
directTo, directTo,
chatReference, chatReference,
messageText, messageText,
otherData, otherData,
publicKeyOfRecipient, publicKeyOfRecipient,
address: directTo, address: directTo,
}, 120000) },
120000
)
.then(async (response) => { .then(async (response) => {
if (!response?.error) { if (!response?.error) {
if (isNewChatVar) { if (isNewChatVar) {
@ -304,7 +358,7 @@ const sendChatDirect = async ({ chatReference = undefined, messageText, otherDat
try { try {
getRecipientName = await getNameInfo(response.recipient); getRecipientName = await getNameInfo(response.recipient);
} catch (error) { } catch (error) {
console.error("Error fetching recipient name:", error); console.error('Error fetching recipient name:', error);
} }
setSelectedDirect({ setSelectedDirect({
address: response.recipient, address: response.recipient,
@ -315,11 +369,16 @@ const sendChatDirect = async ({ chatReference = undefined, messageText, otherDat
}); });
setNewChat(null); setNewChat(null);
window.sendMessage("addTimestampEnterChat", { window
.sendMessage('addTimestampEnterChat', {
timestamp: Date.now(), timestamp: Date.now(),
groupId: response.recipient, groupId: response.recipient,
}).catch((error) => { })
console.error("Failed to add timestamp:", error.message || "An error occurred"); .catch((error) => {
console.error(
'Failed to add timestamp:',
error.message || 'An error occurred'
);
}); });
setTimeout(() => { setTimeout(() => {
@ -332,32 +391,31 @@ const sendChatDirect = async ({ chatReference = undefined, messageText, otherDat
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) {
throw new Error(error) throw new Error(error);
} finally { } finally {
} }
} };
const clearEditorContent = () => { const clearEditorContent = () => {
if (editorRef.current) { if (editorRef.current) {
setMessageSize(0) setMessageSize(0);
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);
executeEvent("sent-new-message-group", {}) executeEvent('sent-new-message-group', {});
setTimeout(() => { setTimeout(() => {
triggerRerender(); triggerRerender();
}, 300); }, 300);
}, 200); }, 200);
} }
} }
}; };
useEffect(() => { useEffect(() => {
if (!editorRef?.current) return; if (!editorRef?.current) return;
const handleUpdate = () => { const handleUpdate = () => {
const htmlContent = editorRef?.current.getHTML(); const htmlContent = editorRef?.current.getHTML();
@ -373,46 +431,48 @@ useEffect(() => {
return () => { return () => {
editorRef?.current.off('update', handleUpdate); editorRef?.current.off('update', handleUpdate);
}; };
}, [editorRef?.current]); }, [editorRef?.current]);
const sendMessage = async ()=> { const sendMessage = async () => {
try { try {
if(messageSize > 4000) return if (messageSize > 4000) return;
if (+balance < 4)
if(+balance < 4) throw new Error('You need at least 4 QORT to send a message') throw new Error('You need at least 4 QORT to send a message');
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);
pauseAllQueues() pauseAllQueues();
const message = JSON.stringify(htmlContent) const message = JSON.stringify(htmlContent);
if (isNewChat) {
if(isNewChat){ await sendChatDirect({ messageText: htmlContent }, null, null, true);
await sendChatDirect({ messageText: htmlContent}, null, null, true) return;
return
} }
let repliedTo = replyMessage?.signature let repliedTo = replyMessage?.signature;
if (replyMessage?.chatReference) { if (replyMessage?.chatReference) {
repliedTo = replyMessage?.chatReference repliedTo = replyMessage?.chatReference;
} }
let chatReference = onEditMessage?.signature let chatReference = onEditMessage?.signature;
const otherData = { const otherData = {
...(onEditMessage?.decryptedData || {}), ...(onEditMessage?.decryptedData || {}),
specialId: uid.rnd(), specialId: uid.rnd(),
repliedTo: onEditMessage ? onEditMessage?.repliedTo : repliedTo, repliedTo: onEditMessage ? onEditMessage?.repliedTo : repliedTo,
type: chatReference ? 'edit' : '' type: chatReference ? 'edit' : '',
} };
const sendMessageFunc = async () => { const sendMessageFunc = async () => {
return await sendChatDirect({ chatReference, messageText: htmlContent, otherData}, selectedDirect?.address, publicKeyOfRecipient, false) return await sendChatDirect(
{ chatReference, messageText: htmlContent, otherData },
selectedDirect?.address,
publicKeyOfRecipient,
false
);
}; };
// Add the function to the queue // Add the function to the queue
const messageObj = { const messageObj = {
@ -423,60 +483,71 @@ useEffect(() => {
...(otherData || {}), ...(otherData || {}),
text: htmlContent, text: htmlContent,
}, },
chatReference chatReference,
} };
addToQueue(sendMessageFunc, messageObj, 'chat-direct', addToQueue(
selectedDirect?.address ); sendMessageFunc,
messageObj,
'chat-direct',
selectedDirect?.address
);
setTimeout(() => { setTimeout(() => {
executeEvent("sent-new-message-group", {}) executeEvent('sent-new-message-group', {});
}, 150); }, 150);
clearEditorContent() clearEditorContent();
setReplyMessage(null) setReplyMessage(null);
setOnEditMessage(null) setOnEditMessage(null);
} }
// send chat message // send chat message
} catch (error) { } catch (error) {
const errorMsg = error?.message || error const errorMsg = error?.message || error;
setInfoSnack({ setInfoSnack({
type: "error", type: 'error',
message: errorMsg === 'invalid signature' ? 'You need at least 4 QORT to send a message' : errorMsg, message:
errorMsg === 'invalid signature'
? 'You need at least 4 QORT to send a message'
: errorMsg,
}); });
setOpenSnack(true); setOpenSnack(true);
console.error(error) console.error(error);
} finally { } finally {
setIsSending(false) setIsSending(false);
resumeAllQueues() resumeAllQueues();
} }
};
const onReply = useCallback(
(message) => {
if (onEditMessage) {
clearEditorContent();
} }
setReplyMessage(message);
setOnEditMessage(null);
editorRef?.current?.chain().focus();
},
[onEditMessage]
);
const onReply = useCallback((message)=> { const onEdit = useCallback((message) => {
if(onEditMessage){ setOnEditMessage(message);
clearEditorContent() setReplyMessage(null);
}
setReplyMessage(message)
setOnEditMessage(null)
editorRef?.current?.chain().focus()
}, [onEditMessage])
const onEdit = useCallback((message)=> {
setOnEditMessage(message)
setReplyMessage(null)
editorRef.current.chain().focus().setContent(message?.text).run(); editorRef.current.chain().focus().setContent(message?.text).run();
}, []);
}, [])
return ( return (
<div style={{ <div
style={{
height: isMobile ? '100%' : '100vh', height: isMobile ? '100%' : '100vh',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
width: '100%', width: '100%',
background: !isMobile && 'var(--bg-2)' background: !isMobile && 'var(--bg-2)',
}}> }}
>
{!isMobile && ( {!isMobile && (
<Box onClick={close} sx={{ <Box
onClick={close}
sx={{
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: '5px', gap: '5px',
@ -486,47 +557,54 @@ useEffect(() => {
borderRadius: '3px', borderRadius: '3px',
background: 'rgb(35, 36, 40)', background: 'rgb(35, 36, 40)',
margin: '10px 0px', margin: '10px 0px',
alignSelf: 'center' alignSelf: 'center',
}}> }}
<ArrowBackIcon sx={{ >
<ArrowBackIcon
sx={{
color: 'white', color: 'white',
fontSize: isMobile ? '20px' : '20px' fontSize: isMobile ? '20px' : '20px',
}}/> }}
<Typography sx={{ />
<Typography
sx={{
color: 'white', color: 'white',
fontSize: isMobile ? '14px' : '14px' fontSize: isMobile ? '14px' : '14px',
}}>Close Direct Chat</Typography> }}
>
Close Direct Chat
</Typography>
</Box> </Box>
)} )}
{isMobile && ( {isMobile && (
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
alignItems: "center", alignItems: 'center',
width: "100%", width: '100%',
marginTop: "14px", marginTop: '14px',
justifyContent: "center", justifyContent: 'center',
height: "15px", height: '15px',
}} }}
> >
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
alignItems: "center", alignItems: 'center',
justifyContent: "space-between", justifyContent: 'space-between',
width: "320px", width: '320px',
}} }}
> >
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
alignItems: "center", alignItems: 'center',
width: "50px", width: '50px',
}} }}
> >
<ButtonBase <ButtonBase
onClick={() => { onClick={() => {
close() close();
}} }}
> >
<ReturnIcon /> <ReturnIcon />
@ -534,25 +612,28 @@ useEffect(() => {
</Box> </Box>
<Typography <Typography
sx={{ sx={{
fontSize: "14px", fontSize: '14px',
fontWeight: 600, fontWeight: 600,
}} }}
> >
{isNewChat ? '' : selectedDirect?.name || (selectedDirect?.address?.slice(0,10) + '...')} {isNewChat
? ''
: selectedDirect?.name ||
selectedDirect?.address?.slice(0, 10) + '...'}
</Typography> </Typography>
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
alignItems: "center", alignItems: 'center',
width: "50px", width: '50px',
justifyContent: "flex-end", justifyContent: 'flex-end',
}} }}
> >
<ButtonBase <ButtonBase
onClick={() => { onClick={() => {
setSelectedDirect(null) setSelectedDirect(null);
setMobileViewModeKeepOpen('') setMobileViewModeKeepOpen('');
setNewChat(false) setNewChat(false);
}} }}
> >
<ExitIcon /> <ExitIcon />
@ -564,21 +645,34 @@ useEffect(() => {
{isNewChat && ( {isNewChat && (
<> <>
<Spacer height="30px" /> <Spacer height="30px" />
<Input sx={{ <Input
sx={{
fontSize: '18px', fontSize: '18px',
padding: '5px' padding: '5px',
}} placeholder='Name or address' value={directToValue} onChange={(e)=> setDirectToValue(e.target.value)} /> }}
placeholder="Name or address"
value={directToValue}
onChange={(e) => setDirectToValue(e.target.value)}
/>
</> </>
)} )}
<ChatList chatReferences={chatReferences} onEdit={onEdit} onReply={onReply} chatId={selectedDirect?.address} initialMessages={messages} myAddress={myAddress} tempMessages={tempMessages} tempChatReferences={tempChatReferences}/> <ChatList
chatReferences={chatReferences}
onEdit={onEdit}
onReply={onReply}
chatId={selectedDirect?.address}
initialMessages={messages}
myAddress={myAddress}
tempMessages={tempMessages}
tempChatReferences={tempChatReferences}
/>
<div
<div style={{ style={{
// position: 'fixed', // position: 'fixed',
// bottom: '0px', // bottom: '0px',
backgroundColor: "#232428", backgroundColor: '#232428',
minHeight: isMobile ? '0px' : '150px', minHeight: isMobile ? '0px' : '150px',
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',
@ -590,32 +684,36 @@ useEffect(() => {
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 style={{ >
<div
style={{
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
flexGrow: isMobile && 1, flexGrow: isMobile && 1,
overflow: !isMobile && "auto", overflow: !isMobile && 'auto',
flexShrink: 0, flexShrink: 0,
width: 'calc(100% - 100px)', width: 'calc(100% - 100px)',
justifyContent: 'flex-end' justifyContent: 'flex-end',
}}> }}
>
{replyMessage && ( {replyMessage && (
<Box sx={{ <Box
sx={{
display: 'flex', display: 'flex',
gap: '5px', gap: '5px',
alignItems: 'flex-start', alignItems: 'flex-start',
width: 'calc(100% - 100px)', width: 'calc(100% - 100px)',
justifyContent: 'flex-end' justifyContent: 'flex-end',
}}> }}
>
<ReplyPreview message={replyMessage} /> <ReplyPreview message={replyMessage} />
<ButtonBase <ButtonBase
onClick={() => { onClick={() => {
setReplyMessage(null) setReplyMessage(null);
setOnEditMessage(null) setOnEditMessage(null);
}} }}
> >
<ExitIcon /> <ExitIcon />
@ -623,22 +721,22 @@ useEffect(() => {
</Box> </Box>
)} )}
{onEditMessage && ( {onEditMessage && (
<Box sx={{ <Box
sx={{
display: 'flex', display: 'flex',
gap: '5px', gap: '5px',
alignItems: 'flex-start', alignItems: 'flex-start',
width: '100%' width: '100%',
}}> }}
>
<ReplyPreview isEdit message={onEditMessage} /> <ReplyPreview isEdit message={onEditMessage} />
<ButtonBase <ButtonBase
onClick={() => { onClick={() => {
setReplyMessage(null) setReplyMessage(null);
setOnEditMessage(null) setOnEditMessage(null);
clearEditorContent()
clearEditorContent();
}} }}
> >
<ExitIcon /> <ExitIcon />
@ -646,24 +744,35 @@ useEffect(() => {
</Box> </Box>
)} )}
<Tiptap isFocusedParent={isFocusedParent} setEditorRef={setEditorRef} onEnter={sendMessage} isChat disableEnter={isMobile ? true : false} setIsFocusedParent={setIsFocusedParent}/> <Tiptap
isFocusedParent={isFocusedParent}
setEditorRef={setEditorRef}
onEnter={sendMessage}
isChat
disableEnter={isMobile ? true : false}
setIsFocusedParent={setIsFocusedParent}
/>
{messageSize > 750 && ( {messageSize > 750 && (
<Box sx={{ <Box
sx={{
display: 'flex', display: 'flex',
width: '100%', width: '100%',
justifyContent: 'flex-start', justifyContent: 'flex-start',
position: 'relative', position: 'relative',
}}> }}
<Typography sx={{ >
<Typography
sx={{
fontSize: '12px', fontSize: '12px',
color: messageSize > 4000 ? 'var(--danger)' : 'unset' color: messageSize > 4000 ? 'var(--danger)' : 'unset',
}}>{`Your message size is of ${messageSize} bytes out of a maximum of 4000`}</Typography> }}
>{`Your message size is of ${messageSize} bytes out of a maximum of 4000`}</Typography>
</Box> </Box>
)} )}
</div> </div>
<Box sx={{ <Box
sx={{
display: 'flex', display: 'flex',
width: '100px', width: '100px',
@ -671,13 +780,12 @@ useEffect(() => {
justifyContent: 'center', justifyContent: 'center',
flexShrink: 0, flexShrink: 0,
position: 'relative', position: 'relative',
}}> }}
>
<CustomButton <CustomButton
onClick={()=> { onClick={() => {
if (isSending) return;
if(isSending) return sendMessage();
sendMessage()
}} }}
style={{ style={{
marginTop: 'auto', marginTop: 'auto',
@ -687,7 +795,7 @@ useEffect(() => {
flexShrink: 0, flexShrink: 0,
padding: '5px', padding: '5px',
width: '100px', width: '100px',
minWidth: 'auto' minWidth: 'auto',
}} }}
> >
{isSending && ( {isSending && (
@ -699,19 +807,26 @@ useEffect(() => {
left: '50%', left: '50%',
marginTop: '-12px', marginTop: '-12px',
marginLeft: '-12px', marginLeft: '-12px',
color: 'white' color: 'white',
}} }}
/> />
)} )}
{` Send`} {` Send`}
</CustomButton> </CustomButton>
</Box> </Box>
</div> </div>
<LoadingSnackbar open={isLoading} info={{ <LoadingSnackbar
message: "Loading chat... please wait." open={isLoading}
}} /> info={{
<CustomizedSnackbars open={openSnack} setOpen={setOpenSnack} info={infoSnack} setInfo={setInfoSnack} /> message: 'Loading chat... please wait.',
}}
/>
<CustomizedSnackbars
open={openSnack}
setOpen={setOpenSnack}
info={infoSnack}
setInfo={setInfoSnack}
/>
</div> </div>
) );
} };

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,7 +51,8 @@ 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
.sendMessage('saveTempPublish', {
data, data,
key, key,
}) })
@ -60,15 +64,15 @@ 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
.sendMessage('getTempPublish', {})
.then((response) => { .then((response) => {
if (!response?.error) { if (!response?.error) {
res(response); res(response);
@ -77,16 +81,16 @@ export const getTempPublish = async () => {
rej(response.error); rej(response.error);
}) })
.catch((error) => { .catch((error) => {
rej(error.message || "An error occurred"); 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
.sendMessage('decryptSingleForPublishes', {
data: encryptedMessages, data: encryptedMessages,
secretKeyObject: secretKey, secretKeyObject: secretKey,
skipDecodeBase64: true, skipDecodeBase64: true,
@ -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,7 +220,8 @@ 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
.sendMessage('encryptSingle', {
data, data,
secretKeyObject, secretKeyObject,
}) })
@ -220,16 +233,16 @@ 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
.sendMessage('publishGroupEncryptedResource', {
encryptedData, encryptedData,
identifier, identifier,
}) })
@ -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 = () => {
@ -266,7 +278,9 @@ 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 || {})
.filter((annKey) => annKey?.startsWith(`grp-${selectedGroup}-anc`))
.map((key) => {
const value = getTempAnnouncements?.announcement[key]; const value = getTempAnnouncements?.announcement[key];
tempData.push(value.data); tempData.push(value.data);
}); });
@ -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 =
isPrivate === false ? null : await getSecretKey(false, true);
const message64: any = await objectToBase64(message); const message64: any = await objectToBase64(message);
const encryptSingle = isPrivate === false ? message64 : await encryptChatMessage( const encryptSingle =
message64, isPrivate === false
secretKeyObject ? 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, name: data.name,
identifier: data.identifier, identifier: data.identifier,
resource: data, resource: data,
}, isPrivate); },
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, name: data.name,
identifier: data.identifier, identifier: data.identifier,
}, isPrivate); },
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 && (
@ -645,13 +678,13 @@ export const GroupAnnouncements = ({
// 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,
}} }}
> >
@ -195,7 +197,7 @@ export const JoinGroup = ({ memberGroups }) => {
{isInGroup && ( {isInGroup && (
<Typography <Typography
sx={{ sx={{
fontSize: "14px", fontSize: '14px',
fontWeight: 600, fontWeight: 600,
}} }}
> >
@ -205,7 +207,7 @@ export const JoinGroup = ({ memberGroups }) => {
{!isInGroup && groupInfo?.isOpen === false && ( {!isInGroup && groupInfo?.isOpen === false && (
<Typography <Typography
sx={{ sx={{
fontSize: "14px", fontSize: '14px',
fontWeight: 600, fontWeight: 600,
}} }}
> >
@ -216,21 +218,23 @@ 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}> }}
disabled={isInGroup}
>
<CustomButtonAccept <CustomButtonAccept
color="black" color="black"
bgColor="var(--green)" bgColor="var(--green)"
sx={{ sx={{
minWidth: "102px", minWidth: '102px',
height: "45px", height: '45px',
fontSize: '16px', fontSize: '16px',
opacity: isInGroup ? 0.1 : 1 opacity: isInGroup ? 0.1 : 1,
}} }}
> >
Join Join
</CustomButtonAccept> </CustomButtonAccept>
@ -240,8 +244,8 @@ export const JoinGroup = ({ memberGroups }) => {
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,9 +47,9 @@ 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);
@ -60,17 +60,17 @@ export const Minting = ({
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' });
}); });
}); });
}, []); }, []);
@ -345,7 +345,6 @@ export const Minting = ({
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) =>
@ -356,11 +355,10 @@ export const Minting = ({
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,12 +796,13 @@ 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
patient, this could take up to 90 seconds.
</Typography> </Typography>
)} )}
{isShowNext && ( {isShowNext && (
@ -814,16 +810,18 @@ export const Minting = ({
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,47 +1,50 @@
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 () => {
const sendCoinFunc = async() => {
try { try {
setSendPaymentError(""); setSendPaymentError('');
setSendPaymentSuccess(""); setSendPaymentSuccess('');
if (!paymentTo) { if (!paymentTo) {
setSendPaymentError("Please enter a recipient"); setSendPaymentError('Please enter a recipient');
return; return;
} }
if (!paymentAmount) { if (!paymentAmount) {
setSendPaymentError("Please enter an amount greater than 0"); setSendPaymentError('Please enter an amount greater than 0');
return; return;
} }
if (!paymentPassword) { if (!paymentPassword) {
setSendPaymentError("Please enter your wallet password"); setSendPaymentError('Please enter your wallet password');
return; return;
} }
const fee = await getFee('PAYMENT') const fee = await getFee('PAYMENT');
await show({ await show({
message: `Would you like to transfer ${Number(paymentAmount)} QORT?` , message: `Would you like to transfer ${Number(paymentAmount)} QORT?`,
paymentFee: fee.fee + ' QORT' paymentFee: fee.fee + ' QORT',
}) });
setIsLoadingSendCoin(true); setIsLoadingSendCoin(true);
window window
.sendMessage("sendCoin", { .sendMessage('sendCoin', {
amount: Number(paymentAmount), amount: Number(paymentAmount),
receiver: paymentTo.trim(), receiver: paymentTo.trim(),
password: paymentPassword, password: paymentPassword,
@ -50,13 +53,12 @@ export const QortPayment = ({balance, show, onSuccess, defaultPaymentTo}) => {
if (response?.error) { if (response?.error) {
setSendPaymentError(response.error); setSendPaymentError(response.error);
} else { } else {
onSuccess() onSuccess();
} }
setIsLoadingSendCoin(false); setIsLoadingSendCoin(false);
}) })
.catch((error) => { .catch((error) => {
console.error("Failed to send coin:", error); console.error('Failed to send coin:', error);
setIsLoadingSendCoin(false); setIsLoadingSendCoin(false);
}); });
} catch (error) { } catch (error) {
@ -67,16 +69,16 @@ export const QortPayment = ({balance, show, onSuccess, defaultPaymentTo}) => {
<> <>
<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,
}} }}
> >
@ -85,20 +87,20 @@ export const QortPayment = ({balance, show, onSuccess, defaultPaymentTo}) => {
<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,
}} }}
> >
@ -117,9 +119,7 @@ export const QortPayment = ({balance, show, onSuccess, defaultPaymentTo}) => {
autoComplete="off" autoComplete="off"
/> />
<Spacer height="6px" /> <Spacer height="6px" />
<CustomLabel htmlFor="standard-adornment-amount"> <CustomLabel htmlFor="standard-adornment-amount">Amount</CustomLabel>
Amount
</CustomLabel>
<Spacer height="5px" /> <Spacer height="5px" />
<BoundedNumericTextField <BoundedNumericTextField
value={paymentAmount} value={paymentAmount}
@ -148,20 +148,23 @@ export const QortPayment = ({balance, show, onSuccess, defaultPaymentTo}) => {
<Spacer height="25px" /> <Spacer height="25px" />
<CustomButton <CustomButton
sx={{ sx={{
cursor: isLoadingSendCoin ? 'default' : 'pointer' cursor: isLoadingSendCoin ? 'default' : 'pointer',
}} }}
onClick={() => { onClick={() => {
if(isLoadingSendCoin) return if (isLoadingSendCoin) return;
sendCoinFunc(); sendCoinFunc();
}} }}
> >
{isLoadingSendCoin && ( {isLoadingSendCoin && (
<CircularProgress size={16} sx={{ <CircularProgress
color: 'white' size={16}
}} /> sx={{
color: 'white',
}}
/>
)} )}
Send Send
</CustomButton> </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;