mirror of
https://github.com/Qortal/chrome-extension.git
synced 2025-02-11 17:55:49 +00:00
batch of updates 4
This commit is contained in:
parent
d1be494420
commit
69888c271a
@ -137,6 +137,47 @@ border-radius: 5px;
|
||||
}
|
||||
`;
|
||||
|
||||
interface CustomButtonProps {
|
||||
bgColor?: string;
|
||||
color?: string;
|
||||
}
|
||||
export const CustomButtonAccept = styled(Box)<CustomButtonProps>(
|
||||
({ bgColor, color }) => ({
|
||||
boxSizing: "border-box",
|
||||
padding: "15px 20px",
|
||||
gap: "10px",
|
||||
border: "0.5px solid rgba(255, 255, 255, 0.5)",
|
||||
filter: "drop-shadow(1px 4px 10.5px rgba(0,0,0,0.3))",
|
||||
borderRadius: 5,
|
||||
display: "inline-flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
width: "fit-content",
|
||||
transition: "all 0.2s",
|
||||
minWidth: 160,
|
||||
cursor: "pointer",
|
||||
fontWeight: 600,
|
||||
fontFamily: "Inter",
|
||||
textAlign: "center",
|
||||
opacity: 0.7,
|
||||
// Use the passed-in props or fallback defaults
|
||||
backgroundColor: bgColor || "transparent",
|
||||
color: color || "white",
|
||||
|
||||
"&:hover": {
|
||||
opacity: 1,
|
||||
backgroundColor: bgColor
|
||||
? bgColor
|
||||
: "rgba(41, 41, 43, 1)", // fallback hover bg
|
||||
color: color || "white",
|
||||
svg: {
|
||||
path: {
|
||||
fill: color || "white",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
export const CustomInput = styled(TextField)({
|
||||
width: "183px", // Adjust the width as needed
|
||||
|
105
src/App.tsx
105
src/App.tsx
@ -61,6 +61,7 @@ import {
|
||||
AuthenticatedContainerInnerLeft,
|
||||
AuthenticatedContainerInnerRight,
|
||||
CustomButton,
|
||||
CustomButtonAccept,
|
||||
CustomInput,
|
||||
CustomLabel,
|
||||
TextItalic,
|
||||
@ -319,6 +320,14 @@ function App() {
|
||||
const { isShow: isShowUnsavedChanges, onCancel: onCancelUnsavedChanges, onOk: onOkUnsavedChanges, show: showUnsavedChanges, message: messageUnsavedChanges } = useModal();
|
||||
const {downloadResource} = useFetchResources()
|
||||
|
||||
const {
|
||||
isShow: isShowInfo,
|
||||
onCancel: onCancelInfo,
|
||||
onOk: onOkInfo,
|
||||
show: showInfo,
|
||||
message: messageInfo,
|
||||
} = useModal();
|
||||
|
||||
const {
|
||||
onCancel: onCancelQortalRequest,
|
||||
onOk: onOkQortalRequest,
|
||||
@ -355,6 +364,13 @@ function App() {
|
||||
const { toggleFullScreen } = useAppFullScreen(setFullScreen);
|
||||
const {showTutorial, openTutorialModal, shownTutorialsInitiated, setOpenTutorialModal} = useHandleTutorials()
|
||||
|
||||
const passwordRef = useRef<HTMLInputElement>(null);
|
||||
useEffect(() => {
|
||||
if (extState === "wallet-dropped" && passwordRef.current) {
|
||||
passwordRef.current.focus();
|
||||
}
|
||||
}, [extState]);
|
||||
|
||||
useEffect(() => {
|
||||
// Attach a global event listener for double-click
|
||||
const handleDoubleClick = () => {
|
||||
@ -1618,7 +1634,12 @@ function App() {
|
||||
show,
|
||||
message,
|
||||
rootHeight,
|
||||
downloadResource
|
||||
downloadResource,
|
||||
showInfo,
|
||||
openSnackGlobal: openSnack,
|
||||
setOpenSnackGlobal: setOpenSnack,
|
||||
infoSnackCustom: infoSnack,
|
||||
setInfoSnackCustom: setInfoSnack,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
@ -1763,6 +1784,7 @@ function App() {
|
||||
value={paymentPassword}
|
||||
onChange={(e) => setPaymentPassword(e.target.value)}
|
||||
autoComplete="off"
|
||||
ref={passwordRef}
|
||||
/>
|
||||
</Box>
|
||||
<Spacer height="10px" />
|
||||
@ -2636,11 +2658,48 @@ function App() {
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button variant="contained" onClick={onCancel}>
|
||||
Disagree
|
||||
<Button sx={{
|
||||
backgroundColor: 'var(--green)',
|
||||
color: 'black',
|
||||
opacity: 0.7,
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--green)',
|
||||
color: 'black',
|
||||
opacity: 1
|
||||
},
|
||||
}} variant="contained" onClick={onOk} autoFocus>
|
||||
accept
|
||||
</Button>
|
||||
<Button variant="contained" onClick={onOk} autoFocus>
|
||||
Agree
|
||||
<Button sx={{
|
||||
backgroundColor: 'var(--unread)',
|
||||
color: 'black',
|
||||
opacity: 0.7,
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--unread)',
|
||||
color: 'black',
|
||||
opacity: 1
|
||||
},
|
||||
}} variant="contained" onClick={onCancel}>
|
||||
decline
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)}
|
||||
{isShowInfo && (
|
||||
<Dialog
|
||||
open={isShowInfo}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">{"Important Info"}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
{messageInfo.message}
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button variant="contained" onClick={onOkInfo} autoFocus>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
@ -2892,22 +2951,26 @@ function App() {
|
||||
gap: "14px",
|
||||
}}
|
||||
>
|
||||
<CustomButton
|
||||
sx={{
|
||||
minWidth: "102px",
|
||||
}}
|
||||
onClick={() => onOkQortalRequestExtension("accepted")}
|
||||
>
|
||||
accept
|
||||
</CustomButton>
|
||||
<CustomButton
|
||||
sx={{
|
||||
minWidth: "102px",
|
||||
}}
|
||||
onClick={() => onCancelQortalRequestExtension()}
|
||||
>
|
||||
decline
|
||||
</CustomButton>
|
||||
<CustomButtonAccept
|
||||
color="black"
|
||||
bgColor="var(--green)"
|
||||
sx={{
|
||||
minWidth: "102px",
|
||||
}}
|
||||
onClick={() => onOkQortalRequestExtension("accepted")}
|
||||
>
|
||||
accept
|
||||
</CustomButtonAccept>
|
||||
<CustomButtonAccept
|
||||
color="black"
|
||||
bgColor="var(--unread)"
|
||||
sx={{
|
||||
minWidth: "102px",
|
||||
}}
|
||||
onClick={() => onCancelQortalRequestExtension()}
|
||||
>
|
||||
decline
|
||||
</CustomButtonAccept>
|
||||
</Box>
|
||||
<ErrorText>{sendPaymentError}</ErrorText>
|
||||
</Box>
|
||||
|
15
src/assets/Icons/AdminsIcon.tsx
Normal file
15
src/assets/Icons/AdminsIcon.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
export const AdminsIcon= ({ color = 'white', height = 48, width = 50 }) => {
|
||||
return (
|
||||
<svg width={width} height={height} viewBox="0 0 50 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.278 10.622C19.278 8.52117 19.901 6.46752 21.0681 4.72074C22.2353 2.97396 23.8942 1.61251 25.8351 0.808557C27.7761 0.00460249 29.9118 -0.205749 31.9722 0.204103C34.0327 0.613956 35.9254 1.6256 37.4109 3.11112C38.8964 4.59663 39.908 6.48929 40.3179 8.54976C40.7278 10.6102 40.5174 12.746 39.7134 14.6869C38.9095 16.6278 37.548 18.2867 35.8013 19.4539C34.0545 20.621 32.0008 21.244 29.9 21.244C27.0838 21.2408 24.3839 20.1207 22.3926 18.1294C20.4013 16.1381 19.2812 13.4382 19.278 10.622ZM17.16 37.736C17.3428 37.8483 17.5137 37.979 17.67 38.126L17.5 40.426C17.3249 40.549 17.1372 40.6529 16.94 40.736C16.6401 40.6901 16.3332 40.7363 16.0601 40.8684C15.7869 41.0005 15.5603 41.2124 15.41 41.476L14.54 42.996C14.3907 43.2597 14.325 43.5625 14.3518 43.8644C14.3786 44.1662 14.4966 44.4528 14.69 44.686C14.7144 44.8985 14.7177 45.1129 14.7 45.326L12.8 46.636C12.5952 46.5775 12.3974 46.4971 12.21 46.396C12.0564 46.1331 11.8277 45.9221 11.5533 45.7903C11.2788 45.6584 10.9712 45.6116 10.67 45.656L8.95 45.926C8.6485 45.9709 8.36799 46.1071 8.14628 46.3163C7.92457 46.5255 7.7723 46.7976 7.71 47.096C7.56006 47.2479 7.39599 47.3852 7.22 47.506L5.01 46.826C4.92855 46.6296 4.86822 46.4251 4.83 46.216C4.94414 45.9337 4.9689 45.6231 4.90095 45.3263C4.83299 45.0294 4.67559 44.7606 4.45 44.556L3.17 43.376C2.94532 43.1703 2.66372 43.0373 2.36214 42.9945C2.06055 42.9517 1.75306 43.001 1.48 43.136C1.26901 43.1167 1.06096 43.0731 0.86 43.006L0 40.866C0.107104 40.6785 0.230942 40.5012 0.37 40.336C0.662067 40.2503 0.920588 40.0766 1.11029 39.8386C1.3 39.6006 1.41164 39.3098 1.43 39.006L1.56 37.266C1.58108 36.9627 1.50882 36.6603 1.35293 36.3993C1.19705 36.1383 0.96501 35.9312 0.688 35.806C0.579009 35.6225 0.482136 35.4321 0.398 35.236L1.548 33.226C1.75938 33.1906 1.97386 33.1772 2.188 33.186C2.43756 33.3593 2.73415 33.4522 3.038 33.4522C3.34185 33.4522 3.63844 33.3593 3.888 33.186L5.328 32.196C5.57858 32.0241 5.77044 31.7795 5.87773 31.4952C5.98501 31.2109 6.00255 30.9006 5.928 30.606C5.99996 30.4048 6.09034 30.2106 6.198 30.026L8.478 29.676C8.63866 29.8159 8.78292 29.9736 8.908 30.146C8.92721 30.4496 9.03917 30.7399 9.22874 30.9778C9.41832 31.2156 9.67637 31.3896 9.968 31.476L11.638 31.986C11.9289 32.0736 12.2396 32.0708 12.5289 31.9779C12.8181 31.8851 13.0724 31.7065 13.258 31.466C13.46 31.3985 13.6675 31.3484 13.878 31.316L15.578 32.876C15.5633 33.0885 15.5298 33.2994 15.478 33.506C15.2538 33.7116 15.0975 33.9804 15.0296 34.2769C14.9617 34.5733 14.9856 34.8834 15.098 35.166L15.738 36.786C15.8515 37.0693 16.0481 37.3116 16.3019 37.4812C16.5557 37.6507 16.8548 37.7396 17.16 37.736ZM10.51 38.156C10.3773 37.678 10.0609 37.2718 9.63 37.026C9.35098 36.8624 9.03343 36.7762 8.71 36.776C8.29776 36.7752 7.89685 36.9109 7.56974 37.1618C7.24264 37.4126 7.0077 37.7647 6.90156 38.163C6.79541 38.5613 6.82401 38.9836 6.9829 39.364C7.14179 39.7444 7.42205 40.0615 7.78 40.266C7.99021 40.3909 8.2238 40.4713 8.46633 40.5022C8.70886 40.5332 8.95516 40.5141 9.19 40.446C9.42745 40.3851 9.65005 40.2765 9.8443 40.127C10.0385 39.9774 10.2004 39.79 10.32 39.576C10.4445 39.3633 10.5255 39.1279 10.5581 38.8836C10.5907 38.6393 10.5744 38.3909 10.51 38.153V38.156ZM44.168 29.124C40.8 25.786 35.977 24.165 29.929 24.268C23.891 24.168 19.148 25.746 15.782 28.986L18.104 31.122C18.3885 31.3825 18.5612 31.743 18.586 32.128C18.6519 33.0412 18.5153 33.9577 18.186 34.812L18.232 34.931C19.0589 35.3243 19.7879 35.8969 20.366 36.607C20.612 36.904 20.7341 37.2843 20.707 37.669L20.451 41.298H48.374C48.518 41.2982 48.6604 41.2672 48.7913 41.2073C48.9223 41.1474 49.0387 41.0599 49.1327 40.9508C49.2267 40.8416 49.2961 40.7135 49.336 40.5752C49.3759 40.4368 49.3855 40.2914 49.364 40.149C48.8358 36.0186 47.0175 32.1604 44.168 29.124Z" fill={color}/>
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
);
|
||||
};
|
||||
|
@ -6,6 +6,7 @@ import { constant, isArray } from "lodash";
|
||||
import {
|
||||
decryptGroupEncryption,
|
||||
encryptAndPublishSymmetricKeyGroupChat,
|
||||
encryptAndPublishSymmetricKeyGroupChatForAdmins,
|
||||
publishGroupEncryptedResource,
|
||||
publishOnQDN,
|
||||
uint8ArrayToObject,
|
||||
@ -2549,8 +2550,7 @@ async function createGroup({
|
||||
const signedBytes = Base58.encode(tx.signedBytes);
|
||||
|
||||
const res = await processTransactionVersion2(signedBytes);
|
||||
if (!res?.signature)
|
||||
throw new Error("Transaction was not able to be processed");
|
||||
if (!res?.signature) throw new Error(res?.message || "Transaction was not able to be processed");
|
||||
return res;
|
||||
}
|
||||
async function inviteToGroup({ groupId, qortalAddress, inviteTime }) {
|
||||
@ -4289,6 +4289,22 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => {
|
||||
|
||||
break;
|
||||
}
|
||||
case "encryptAndPublishSymmetricKeyGroupChatForAdmins": {
|
||||
const { groupId, previousData, admins } = request.payload;
|
||||
|
||||
encryptAndPublishSymmetricKeyGroupChatForAdmins({
|
||||
groupId, previousData, admins
|
||||
})
|
||||
.then((data) => {
|
||||
sendResponse(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error.message);
|
||||
sendResponse({ error: error.message });
|
||||
});
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case "publishGroupEncryptedResource": {
|
||||
const { encryptedData, identifier } = request.payload;
|
||||
|
||||
|
@ -83,8 +83,80 @@ const getPublicKeys = async (groupNumber: number) => {
|
||||
return members
|
||||
}
|
||||
|
||||
export const getPublicKeysByAddress = async (admins) => {
|
||||
const validApi = await getBaseApi()
|
||||
|
||||
|
||||
let members: any = [];
|
||||
if (Array.isArray(admins)) {
|
||||
for (const address of admins) {
|
||||
if (address) {
|
||||
const resAddress = await fetch(`${validApi}/addresses/${address}`);
|
||||
const resData = await resAddress.json();
|
||||
const publicKey = resData.publicKey;
|
||||
members.push(publicKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return members
|
||||
}
|
||||
export const encryptAndPublishSymmetricKeyGroupChatForAdmins = async ({groupId, previousData, admins}: {
|
||||
groupId: number,
|
||||
previousData: Object,
|
||||
}) => {
|
||||
try {
|
||||
console.log({groupId, previousData, admins})
|
||||
let highestKey = 0
|
||||
if(previousData){
|
||||
highestKey = Math.max(...Object.keys((previousData || {})).filter(item=> !isNaN(+item)).map(Number));
|
||||
|
||||
}
|
||||
|
||||
const resKeyPair = await getKeyPair()
|
||||
const parsedData = JSON.parse(resKeyPair)
|
||||
|
||||
const privateKey = parsedData.privateKey
|
||||
const userPublicKey = parsedData.publicKey
|
||||
const groupmemberPublicKeys = await getPublicKeysByAddress(admins.map((admin)=> admin.address))
|
||||
|
||||
|
||||
const symmetricKey = createSymmetricKeyAndNonce()
|
||||
const nextNumber = highestKey + 1
|
||||
const objectToSave = {
|
||||
...previousData,
|
||||
[nextNumber]: symmetricKey
|
||||
}
|
||||
|
||||
const symmetricKeyAndNonceBase64 = await objectToBase64(objectToSave)
|
||||
console.log({ data64: symmetricKeyAndNonceBase64,
|
||||
publicKeys: groupmemberPublicKeys,
|
||||
privateKey,
|
||||
userPublicKey})
|
||||
const encryptedData = encryptDataGroup({
|
||||
data64: symmetricKeyAndNonceBase64,
|
||||
publicKeys: groupmemberPublicKeys,
|
||||
privateKey,
|
||||
userPublicKey
|
||||
})
|
||||
if(encryptedData){
|
||||
const registeredName = await getNameInfo()
|
||||
const data = await publishData({
|
||||
registeredName, file: encryptedData, service: 'DOCUMENT_PRIVATE', identifier: `admins-symmetric-qchat-group-${groupId}`, uploadType: 'file', isBase64: true, withFee: true
|
||||
})
|
||||
return {
|
||||
data,
|
||||
numberOfMembers: groupmemberPublicKeys.length
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new Error('Cannot encrypt content')
|
||||
}
|
||||
} catch (error: any) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
export const encryptAndPublishSymmetricKeyGroupChat = async ({groupId, previousData}: {
|
||||
groupId: number,
|
||||
previousData: Object,
|
||||
|
@ -22,7 +22,7 @@ import { useRecoilState, useSetRecoilState } from "recoil";
|
||||
import { settingsLocalLastUpdatedAtom, sortablePinnedAppsAtom } from "../../atoms/global";
|
||||
import { saveToLocalStorage } from "./AppsNavBar";
|
||||
|
||||
export const AppInfoSnippet = ({ app, myName, isFromCategory }) => {
|
||||
export const AppInfoSnippet = ({ app, myName, isFromCategory, parentStyles = {} }) => {
|
||||
|
||||
const isInstalled = app?.status?.status === 'READY'
|
||||
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(sortablePinnedAppsAtom);
|
||||
@ -30,7 +30,9 @@ export const AppInfoSnippet = ({ app, myName, isFromCategory }) => {
|
||||
const isSelectedAppPinned = !!sortablePinnedApps?.find((item)=> item?.name === app?.name && item?.service === app?.service)
|
||||
const setSettingsLocalLastUpdated = useSetRecoilState(settingsLocalLastUpdatedAtom);
|
||||
return (
|
||||
<AppInfoSnippetContainer>
|
||||
<AppInfoSnippetContainer sx={{
|
||||
...parentStyles
|
||||
}}>
|
||||
<AppInfoSnippetLeft>
|
||||
<ButtonBase
|
||||
sx={{
|
||||
|
@ -84,6 +84,8 @@ export const AppsCategoryDesktop = ({
|
||||
const { rootHeight } = useContext(MyContext);
|
||||
|
||||
const categoryList = useMemo(() => {
|
||||
if(category?.id === 'all') return availableQapps
|
||||
|
||||
return availableQapps.filter(
|
||||
(app) => app?.metadata?.category === category?.id
|
||||
);
|
||||
@ -96,7 +98,11 @@ export const AppsCategoryDesktop = ({
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedValue(searchValue);
|
||||
}, 350);
|
||||
|
||||
setTimeout(() => {
|
||||
virtuosoRef.current.scrollToIndex({
|
||||
index: 0
|
||||
});
|
||||
}, 500);
|
||||
// Cleanup timeout if searchValue changes before the timeout completes
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
@ -108,7 +114,7 @@ export const AppsCategoryDesktop = ({
|
||||
const searchedList = useMemo(() => {
|
||||
if (!debouncedValue) return categoryList;
|
||||
return categoryList.filter((app) =>
|
||||
app.name.toLowerCase().includes(debouncedValue.toLowerCase())
|
||||
app.name.toLowerCase().includes(debouncedValue.toLowerCase()) || (app?.metadata?.title && app?.metadata?.title?.toLowerCase().includes(debouncedValue.toLowerCase()))
|
||||
);
|
||||
}, [debouncedValue, categoryList]);
|
||||
|
||||
@ -120,6 +126,9 @@ export const AppsCategoryDesktop = ({
|
||||
app={app}
|
||||
myName={myName}
|
||||
isFromCategory={true}
|
||||
parentStyles={{
|
||||
padding: '0px 10px'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -193,7 +202,7 @@ export const AppsCategoryDesktop = ({
|
||||
<AppsWidthLimiter>
|
||||
<StyledVirtuosoContainer
|
||||
sx={{
|
||||
height: rootHeight,
|
||||
height: `calc(100vh - 36px - 90px - 25px)`
|
||||
}}
|
||||
>
|
||||
<Virtuoso
|
||||
@ -202,9 +211,9 @@ export const AppsCategoryDesktop = ({
|
||||
itemContent={rowRenderer}
|
||||
atBottomThreshold={50}
|
||||
followOutput="smooth"
|
||||
components={{
|
||||
Scroller: ScrollerStyled, // Use the styled scroller component
|
||||
}}
|
||||
// components={{
|
||||
// Scroller: ScrollerStyled, // Use the styled scroller component
|
||||
// }}
|
||||
/>
|
||||
</StyledVirtuosoContainer>
|
||||
</AppsWidthLimiter>
|
||||
|
@ -7,13 +7,15 @@ import {
|
||||
AppsContainer,
|
||||
AppsParent,
|
||||
} from "./Apps-styles";
|
||||
import { Avatar, ButtonBase } from "@mui/material";
|
||||
import { Avatar, Box, ButtonBase, Input } from "@mui/material";
|
||||
import { Add } from "@mui/icons-material";
|
||||
import { getBaseApiReact, isMobile } from "../../App";
|
||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { SortablePinnedApps } from "./SortablePinnedApps";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { extractComponents } from "../Chat/MessageDisplay";
|
||||
import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward';
|
||||
|
||||
export const AppsHomeDesktop = ({
|
||||
setMode,
|
||||
@ -21,6 +23,22 @@ export const AppsHomeDesktop = ({
|
||||
myWebsite,
|
||||
availableQapps,
|
||||
}) => {
|
||||
const [qortalUrl, setQortalUrl] = useState('')
|
||||
|
||||
const openQortalUrl = ()=> {
|
||||
try {
|
||||
if(!qortalUrl) return
|
||||
const res = extractComponents(qortalUrl);
|
||||
if (res) {
|
||||
const { service, name, identifier, path } = res;
|
||||
executeEvent("addTab", { data: { service, name, identifier, path } });
|
||||
executeEvent("open-apps-mode", { });
|
||||
setQortalUrl('qortal://')
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<AppsContainer
|
||||
@ -37,6 +55,65 @@ export const AppsHomeDesktop = ({
|
||||
Apps Dashboard
|
||||
</AppLibrarySubTitle>
|
||||
</AppsContainer>
|
||||
<Spacer height="20px" />
|
||||
<AppsContainer
|
||||
sx={{
|
||||
|
||||
justifyContent: "flex-start",
|
||||
|
||||
}}
|
||||
>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#1f2023',
|
||||
padding: '7px',
|
||||
borderRadius: '20px',
|
||||
width: '100%',
|
||||
maxWidth: '500px'
|
||||
}}>
|
||||
<Input
|
||||
id="standard-adornment-name"
|
||||
value={qortalUrl}
|
||||
onChange={(e) => {
|
||||
setQortalUrl(e.target.value)
|
||||
}}
|
||||
disableUnderline
|
||||
autoComplete='off'
|
||||
autoCorrect='off'
|
||||
placeholder="qortal://"
|
||||
sx={{
|
||||
width: '100%',
|
||||
color: 'white',
|
||||
'& .MuiInput-input::placeholder': {
|
||||
color: 'rgba(84, 84, 84, 0.70) !important',
|
||||
fontSize: '20px',
|
||||
fontStyle: 'normal',
|
||||
fontWeight: 400,
|
||||
lineHeight: '120%', // 24px
|
||||
letterSpacing: '0.15px',
|
||||
opacity: 1
|
||||
},
|
||||
'&:focus': {
|
||||
outline: 'none',
|
||||
},
|
||||
// Add any additional styles for the input here
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && qortalUrl) {
|
||||
console.log('hello')
|
||||
openQortalUrl();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<ButtonBase onClick={()=> openQortalUrl()}>
|
||||
<ArrowOutwardIcon sx={{
|
||||
color: qortalUrl ? 'white' : 'rgba(84, 84, 84, 0.70)'
|
||||
}} />
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
</AppsContainer>
|
||||
<Spacer height="45px" />
|
||||
<AppsContainer
|
||||
sx={{
|
||||
|
@ -24,7 +24,7 @@ import {
|
||||
PublishQAppCTARight,
|
||||
PublishQAppDotsBG,
|
||||
} from "./Apps-styles";
|
||||
import { Avatar, Box, ButtonBase, InputBase, styled } from "@mui/material";
|
||||
import { Avatar, Box, ButtonBase, InputBase, styled, Typography } from "@mui/material";
|
||||
import { Add } from "@mui/icons-material";
|
||||
import { MyContext, getBaseApiReact } from "../../App";
|
||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
@ -121,7 +121,11 @@ export const AppsLibraryDesktop = ({
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedValue(searchValue);
|
||||
}, 350);
|
||||
|
||||
setTimeout(() => {
|
||||
virtuosoRef.current.scrollToIndex({
|
||||
index: 0
|
||||
});
|
||||
}, 500);
|
||||
// Cleanup timeout if searchValue changes before the timeout completes
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
@ -133,7 +137,7 @@ export const AppsLibraryDesktop = ({
|
||||
const searchedList = useMemo(() => {
|
||||
if (!debouncedValue) return [];
|
||||
return availableQapps.filter((app) =>
|
||||
app.name.toLowerCase().includes(debouncedValue.toLowerCase())
|
||||
app.name.toLowerCase().includes(debouncedValue.toLowerCase()) || (app?.metadata?.title && app?.metadata?.title?.toLowerCase().includes(debouncedValue.toLowerCase()))
|
||||
);
|
||||
}, [debouncedValue]);
|
||||
|
||||
@ -144,6 +148,9 @@ export const AppsLibraryDesktop = ({
|
||||
key={`${app?.service}-${app?.name}`}
|
||||
app={app}
|
||||
myName={myName}
|
||||
parentStyles={{
|
||||
padding: '0px 10px'
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -259,7 +266,7 @@ export const AppsLibraryDesktop = ({
|
||||
<AppsWidthLimiter>
|
||||
<StyledVirtuosoContainer
|
||||
sx={{
|
||||
height: `calc(100vh - 36px - 90px)`,
|
||||
height: `calc(100vh - 36px - 90px - 90px)`,
|
||||
}}
|
||||
>
|
||||
<Virtuoso
|
||||
@ -268,12 +275,16 @@ export const AppsLibraryDesktop = ({
|
||||
itemContent={rowRenderer}
|
||||
atBottomThreshold={50}
|
||||
followOutput="smooth"
|
||||
components={{
|
||||
Scroller: ScrollerStyled, // Use the styled scroller component
|
||||
}}
|
||||
// components={{
|
||||
// Scroller: ScrollerStyled, // Use the styled scroller component
|
||||
// }}
|
||||
/>
|
||||
</StyledVirtuosoContainer>
|
||||
</AppsWidthLimiter>
|
||||
) : searchedList?.length === 0 && debouncedValue ? (
|
||||
<AppsWidthLimiter>
|
||||
<Typography>No results</Typography>
|
||||
</AppsWidthLimiter>
|
||||
) : (
|
||||
<>
|
||||
<AppLibrarySubTitle
|
||||
@ -409,6 +420,32 @@ export const AppsLibraryDesktop = ({
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
|
||||
onClick={() => {
|
||||
executeEvent("selectedCategory", {
|
||||
data: {
|
||||
id: 'all',
|
||||
name: 'All'
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: "60px",
|
||||
padding: "0px 24px",
|
||||
border: "4px solid #10242F",
|
||||
borderRadius: "6px",
|
||||
boxShadow: "2px 4px 0px 0px #000000",
|
||||
}}
|
||||
>
|
||||
All
|
||||
</Box>
|
||||
</ButtonBase>
|
||||
{categories?.map((category) => {
|
||||
return (
|
||||
<ButtonBase
|
||||
|
64
src/components/Chat/AdminSpace.tsx
Normal file
64
src/components/Chat/AdminSpace.tsx
Normal file
@ -0,0 +1,64 @@
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { MyContext, isMobile } from "../../App";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { AdminSpaceInner } from "./AdminSpaceInner";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const AdminSpace = ({
|
||||
selectedGroup,
|
||||
adminsWithNames,
|
||||
userInfo,
|
||||
secretKey,
|
||||
getSecretKey,
|
||||
isAdmin,
|
||||
myAddress,
|
||||
hide,
|
||||
defaultThread,
|
||||
setDefaultThread,
|
||||
setIsForceShowCreationKeyPopup
|
||||
}) => {
|
||||
const [isMoved, setIsMoved] = useState(false);
|
||||
useEffect(() => {
|
||||
if (hide) {
|
||||
setTimeout(() => setIsMoved(true), 300); // Wait for the fade-out to complete before moving
|
||||
} else {
|
||||
setIsMoved(false); // Reset the position immediately when showing
|
||||
}
|
||||
}, [hide]);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
// reference to change height
|
||||
height: "calc(100vh - 70px)",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
opacity: hide ? 0 : 1,
|
||||
visibility: hide && 'hidden',
|
||||
position: hide ? 'fixed' : 'relative',
|
||||
left: hide && '-1000px'
|
||||
}}
|
||||
>
|
||||
{!isAdmin && <Box sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
paddingTop: '25px'
|
||||
}}><Typography>Sorry, this space is only for Admins.</Typography></Box>}
|
||||
{isAdmin && <AdminSpaceInner setIsForceShowCreationKeyPopup={setIsForceShowCreationKeyPopup} adminsWithNames={adminsWithNames} selectedGroup={selectedGroup} />}
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
244
src/components/Chat/AdminSpaceInner.tsx
Normal file
244
src/components/Chat/AdminSpaceInner.tsx
Normal file
@ -0,0 +1,244 @@
|
||||
import React, { useCallback, useContext, useEffect, useState } from "react";
|
||||
import {
|
||||
MyContext,
|
||||
getArbitraryEndpointReact,
|
||||
getBaseApiReact,
|
||||
} from "../../App";
|
||||
import { Box, Button, Typography } from "@mui/material";
|
||||
import {
|
||||
decryptResource,
|
||||
getPublishesFromAdmins,
|
||||
validateSecretKey,
|
||||
} from "../Group/Group";
|
||||
import { getFee } from "../../background";
|
||||
import { base64ToUint8Array } from "../../qdn/encryption/group-encryption";
|
||||
import { uint8ArrayToObject } from "../../backgroundFunctions/encryption";
|
||||
import { formatTimestampForum } from "../../utils/time";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
|
||||
export const getPublishesFromAdminsAdminSpace = async (
|
||||
admins: string[],
|
||||
groupId
|
||||
) => {
|
||||
const queryString = admins.map((name) => `name=${name}`).join("&");
|
||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT_PRIVATE&identifier=admins-symmetric-qchat-group-${groupId}&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`;
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error("network error");
|
||||
}
|
||||
const adminData = await response.json();
|
||||
|
||||
const filterId = adminData.filter(
|
||||
(data: any) => data.identifier === `admins-symmetric-qchat-group-${groupId}`
|
||||
);
|
||||
if (filterId?.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const sortedData = filterId.sort((a: any, b: any) => {
|
||||
// Get the most recent date for both a and b
|
||||
const dateA = a.updated ? new Date(a.updated) : new Date(a.created);
|
||||
const dateB = b.updated ? new Date(b.updated) : new Date(b.created);
|
||||
|
||||
// Sort by most recent
|
||||
return dateB.getTime() - dateA.getTime();
|
||||
});
|
||||
|
||||
return sortedData[0];
|
||||
};
|
||||
|
||||
export const AdminSpaceInner = ({
|
||||
selectedGroup,
|
||||
adminsWithNames,
|
||||
setIsForceShowCreationKeyPopup,
|
||||
}) => {
|
||||
const [adminGroupSecretKey, setAdminGroupSecretKey] = useState(null);
|
||||
const [isFetchingAdminGroupSecretKey, setIsFetchingAdminGroupSecretKey] =
|
||||
useState(true);
|
||||
const [isFetchingGroupSecretKey, setIsFetchingGroupSecretKey] =
|
||||
useState(true);
|
||||
const [
|
||||
adminGroupSecretKeyPublishDetails,
|
||||
setAdminGroupSecretKeyPublishDetails,
|
||||
] = useState(null);
|
||||
const [groupSecretKeyPublishDetails, setGroupSecretKeyPublishDetails] =
|
||||
useState(null);
|
||||
const [isLoadingPublishKey, setIsLoadingPublishKey] = useState(false);
|
||||
const { show, setTxList, setInfoSnackCustom, setOpenSnackGlobal } =
|
||||
useContext(MyContext);
|
||||
|
||||
const getAdminGroupSecretKey = useCallback(async () => {
|
||||
try {
|
||||
if (!selectedGroup) return;
|
||||
const getLatestPublish = await getPublishesFromAdminsAdminSpace(
|
||||
adminsWithNames.map((admin) => admin?.name),
|
||||
selectedGroup
|
||||
);
|
||||
if (getLatestPublish === false) return;
|
||||
let data;
|
||||
|
||||
const res = await fetch(
|
||||
`${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${
|
||||
getLatestPublish.name
|
||||
}/${getLatestPublish.identifier}?encoding=base64`
|
||||
);
|
||||
data = await res.text();
|
||||
|
||||
const decryptedKey: any = await decryptResource(data);
|
||||
const dataint8Array = base64ToUint8Array(decryptedKey.data);
|
||||
const decryptedKeyToObject = uint8ArrayToObject(dataint8Array);
|
||||
if (!validateSecretKey(decryptedKeyToObject))
|
||||
throw new Error("SecretKey is not valid");
|
||||
setAdminGroupSecretKey(decryptedKeyToObject);
|
||||
setAdminGroupSecretKeyPublishDetails(getLatestPublish);
|
||||
} catch (error) {
|
||||
} finally {
|
||||
setIsFetchingAdminGroupSecretKey(false);
|
||||
}
|
||||
}, [adminsWithNames, selectedGroup]);
|
||||
|
||||
const getGroupSecretKey = useCallback(async () => {
|
||||
try {
|
||||
if (!selectedGroup) return;
|
||||
const getLatestPublish = await getPublishesFromAdmins(
|
||||
adminsWithNames.map((admin) => admin?.name),
|
||||
selectedGroup
|
||||
);
|
||||
if (getLatestPublish === false) setGroupSecretKeyPublishDetails(false);
|
||||
setGroupSecretKeyPublishDetails(getLatestPublish);
|
||||
} catch (error) {
|
||||
} finally {
|
||||
setIsFetchingGroupSecretKey(false);
|
||||
}
|
||||
}, [adminsWithNames, selectedGroup]);
|
||||
|
||||
const createCommonSecretForAdmins = async () => {
|
||||
try {
|
||||
const fee = await getFee("ARBITRARY");
|
||||
await show({
|
||||
message: "Would you like to perform an ARBITRARY transaction?",
|
||||
publishFee: fee.fee + " QORT",
|
||||
});
|
||||
setIsLoadingPublishKey(true);
|
||||
|
||||
|
||||
|
||||
chrome?.runtime?.sendMessage({ action: "encryptAndPublishSymmetricKeyGroupChatForAdmins", payload: {
|
||||
groupId: selectedGroup,
|
||||
previousData: adminGroupSecretKey,
|
||||
admins: adminsWithNames,
|
||||
} }, (response) => {
|
||||
console.log('response', response)
|
||||
if (!response?.error) {
|
||||
setInfoSnackCustom({
|
||||
type: "success",
|
||||
message:
|
||||
"Successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins.",
|
||||
});
|
||||
setOpenSnackGlobal(true);
|
||||
return;
|
||||
} else {
|
||||
setInfoSnackCustom({
|
||||
type: "error",
|
||||
message: response?.error || "unable to re-encrypt secret key",
|
||||
});
|
||||
setOpenSnackGlobal(true);
|
||||
}
|
||||
|
||||
});
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getAdminGroupSecretKey();
|
||||
getGroupSecretKey();
|
||||
}, [getAdminGroupSecretKey, getGroupSecretKey]);
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
padding: "10px",
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<Typography sx={{
|
||||
fontSize: '14px'
|
||||
}}>Reminder: After publishing the key, it will take a couple of minutes for it to appear. Please just wait.</Typography>
|
||||
<Spacer height="25px" />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "20px",
|
||||
width: "300px",
|
||||
maxWidth: "90%",
|
||||
padding: '10px',
|
||||
border: '1px solid gray',
|
||||
borderRadius: '6px'
|
||||
}}
|
||||
>
|
||||
{isFetchingGroupSecretKey && (
|
||||
<Typography>Fetching Group secret key publishes</Typography>
|
||||
)}
|
||||
{!isFetchingGroupSecretKey &&
|
||||
groupSecretKeyPublishDetails === false && (
|
||||
<Typography>No secret key published yet</Typography>
|
||||
)}
|
||||
{groupSecretKeyPublishDetails && (
|
||||
<Typography>
|
||||
Last encryption date:{" "}
|
||||
{formatTimestampForum(
|
||||
groupSecretKeyPublishDetails?.updated ||
|
||||
groupSecretKeyPublishDetails?.created
|
||||
)}{" "}
|
||||
{` by ${groupSecretKeyPublishDetails?.name}`}
|
||||
</Typography>
|
||||
)}
|
||||
<Button disabled={isFetchingGroupSecretKey} onClick={()=> setIsForceShowCreationKeyPopup(true)} variant="contained">
|
||||
Publish group secret key
|
||||
</Button>
|
||||
<Spacer height="20px" />
|
||||
<Typography sx={{
|
||||
fontSize: '14px'
|
||||
}}>This key is to encrypt GROUP related content. This is the only one used in this UI as of now. All group members will be able to see content encrypted with this key.</Typography>
|
||||
</Box>
|
||||
<Spacer height="25px" />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "20px",
|
||||
width: "300px",
|
||||
maxWidth: "90%",
|
||||
padding: '10px',
|
||||
border: '1px solid gray',
|
||||
borderRadius: '6px'
|
||||
}}
|
||||
>
|
||||
{isFetchingAdminGroupSecretKey && (
|
||||
<Typography>Fetching Admins secret key</Typography>
|
||||
)}
|
||||
{!isFetchingAdminGroupSecretKey && !adminGroupSecretKey && (
|
||||
<Typography>No secret key published yet</Typography>
|
||||
)}
|
||||
{adminGroupSecretKeyPublishDetails && (
|
||||
<Typography>
|
||||
Last encryption date:{" "}
|
||||
{formatTimestampForum(
|
||||
adminGroupSecretKeyPublishDetails?.updated ||
|
||||
adminGroupSecretKeyPublishDetails?.created
|
||||
)}
|
||||
</Typography>
|
||||
)}
|
||||
<Button disabled={isFetchingAdminGroupSecretKey} onClick={createCommonSecretForAdmins} variant="contained">
|
||||
Publish admin secret key
|
||||
</Button>
|
||||
<Spacer height="20px" />
|
||||
<Typography sx={{
|
||||
fontSize: '14px'
|
||||
}}>This key is to encrypt ADMIN related content. Only admins would see content encrypted with it.</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -572,7 +572,7 @@ const sendMessage = async ()=> {
|
||||
minHeight: isMobile ? '0px' : '150px',
|
||||
maxHeight: isMobile ? 'auto' : '400px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexDirection: 'row',
|
||||
overflow: 'hidden',
|
||||
width: '100%',
|
||||
boxSizing: 'border-box',
|
||||
@ -588,14 +588,17 @@ const sendMessage = async ()=> {
|
||||
flexDirection: 'column',
|
||||
flexGrow: isMobile && 1,
|
||||
overflow: !isMobile && "auto",
|
||||
flexShrink: 0
|
||||
flexShrink: 0,
|
||||
width: 'calc(100% - 100px)',
|
||||
justifyContent: 'flex-end'
|
||||
}}>
|
||||
{replyMessage && (
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
gap: '5px',
|
||||
alignItems: 'flex-start',
|
||||
width: '100%'
|
||||
width: 'calc(100% - 100px)',
|
||||
justifyContent: 'flex-end'
|
||||
}}>
|
||||
<ReplyPreview message={replyMessage} />
|
||||
|
||||
@ -651,7 +654,7 @@ const sendMessage = async ()=> {
|
||||
</div>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
width: '100&',
|
||||
width: '100px',
|
||||
gap: '10px',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0,
|
||||
@ -690,7 +693,9 @@ const sendMessage = async ()=> {
|
||||
cursor: isSending ? 'default' : 'pointer',
|
||||
background: isSending && 'rgba(0, 0, 0, 0.8)',
|
||||
flexShrink: 0,
|
||||
padding: isMobile && '5px'
|
||||
padding: isMobile && '5px',
|
||||
width: '100px',
|
||||
minWidth: 'auto'
|
||||
}}
|
||||
>
|
||||
{isSending && (
|
||||
|
@ -58,7 +58,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey,
|
||||
|
||||
|
||||
|
||||
const getTimestampEnterChat = async () => {
|
||||
const getTimestampEnterChat = async (selectedGroup) => {
|
||||
try {
|
||||
return new Promise((res, rej) => {
|
||||
chrome?.runtime?.sendMessage(
|
||||
@ -66,8 +66,8 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey,
|
||||
action: "getTimestampEnterChat",
|
||||
},
|
||||
(response) => {
|
||||
if (!response?.error && selectedGroup && response[selectedGroup]) {
|
||||
lastReadTimestamp.current = response[selectedGroup]
|
||||
if(response && selectedGroup){
|
||||
lastReadTimestamp.current = response[selectedGroup] || undefined
|
||||
chrome?.runtime?.sendMessage({
|
||||
action: "addTimestampEnterChat",
|
||||
payload: {
|
||||
@ -89,8 +89,9 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey,
|
||||
};
|
||||
|
||||
useEffect(()=> {
|
||||
getTimestampEnterChat()
|
||||
}, [])
|
||||
if(!selectedGroup) return
|
||||
getTimestampEnterChat(selectedGroup)
|
||||
}, [selectedGroup])
|
||||
|
||||
const openQManager = useCallback(()=> {
|
||||
setIsOpenQManager(true)
|
||||
|
@ -8,7 +8,7 @@ import { decryptResource, getGroupAdmins, validateSecretKey } from '../Group/Gro
|
||||
import { base64ToUint8Array } from '../../qdn/encryption/group-encryption';
|
||||
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
|
||||
|
||||
export const CreateCommonSecret = ({groupId, secretKey, isOwner, myAddress, secretKeyDetails, userInfo, noSecretKey, setHideCommonKeyPopup}) => {
|
||||
export const CreateCommonSecret = ({groupId, secretKey, isOwner, myAddress, secretKeyDetails, userInfo, noSecretKey, setHideCommonKeyPopup, setIsForceShowCreationKeyPopup, isForceShowCreationKeyPopup}) => {
|
||||
const { show, setTxList } = useContext(MyContext);
|
||||
|
||||
const [openSnack, setOpenSnack] = React.useState(false);
|
||||
@ -128,6 +128,9 @@ export const CreateCommonSecret = ({groupId, secretKey, isOwner, myAddress, sec
|
||||
}, ...prev])
|
||||
}
|
||||
setIsLoading(false)
|
||||
setTimeout(() => {
|
||||
setIsForceShowCreationKeyPopup(false)
|
||||
}, 1000);
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@ -144,7 +147,7 @@ export const CreateCommonSecret = ({groupId, secretKey, isOwner, myAddress, sec
|
||||
maxWidth: '350px',
|
||||
background: '#444444'
|
||||
}}>
|
||||
<LoadingButton loading={isLoading} loadingPosition="start" color="warning" variant='contained' onClick={createCommonSecret}>Re-encyrpt key</LoadingButton>
|
||||
<LoadingButton loading={isLoading} loadingPosition="start" color="warning" variant='contained' onClick={createCommonSecret}>Re-encrypt key</LoadingButton>
|
||||
{noSecretKey ? (
|
||||
<Box>
|
||||
<Typography>There is no group secret key. Be the first admin to publish one!</Typography>
|
||||
@ -153,7 +156,7 @@ export const CreateCommonSecret = ({groupId, secretKey, isOwner, myAddress, sec
|
||||
<Box>
|
||||
<Typography>The latest group secret key was published by a non-owner. As the owner of the group please re-encrypt the key as a safeguard</Typography>
|
||||
</Box>
|
||||
): (
|
||||
): isForceShowCreationKeyPopup ? null : (
|
||||
<Box>
|
||||
<Typography>The group member list has changed. Please re-encrypt the secret key.</Typography>
|
||||
</Box>
|
||||
@ -165,6 +168,8 @@ export const CreateCommonSecret = ({groupId, secretKey, isOwner, myAddress, sec
|
||||
}}>
|
||||
<Button onClick={()=> {
|
||||
setHideCommonKeyPopup(true)
|
||||
setIsForceShowCreationKeyPopup(false)
|
||||
|
||||
}} size='small'>Hide</Button>
|
||||
</Box>
|
||||
<CustomizedSnackbars open={openSnack} setOpen={setOpenSnack} info={infoSnack} setInfo={setInfoSnack} />
|
||||
|
@ -18,8 +18,11 @@ import { NotificationIcon2 } from "../../assets/Icons/NotificationIcon2";
|
||||
import { ChatIcon } from "../../assets/Icons/ChatIcon";
|
||||
import { ThreadsIcon } from "../../assets/Icons/ThreadsIcon";
|
||||
import { MembersIcon } from "../../assets/Icons/MembersIcon";
|
||||
import LockIcon from '@mui/icons-material/Lock';
|
||||
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
||||
import { AdminsIcon } from "../../assets/Icons/AdminsIcon";
|
||||
|
||||
const IconWrapper = ({ children, label, color, selected, selectColor }) => {
|
||||
const IconWrapper = ({ children, label, color, selected, selectColor, customHeight }) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@ -28,8 +31,8 @@ const IconWrapper = ({ children, label, color, selected, selectColor }) => {
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
flexDirection: "column",
|
||||
height: "65px",
|
||||
width: "65px",
|
||||
height: customHeight ? customHeight : "65px",
|
||||
width: customHeight ? customHeight : "65px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: selected ? selectColor || "rgba(28, 29, 32, 1)" : "transparent",
|
||||
}}
|
||||
@ -79,7 +82,8 @@ export const DesktopHeader = ({
|
||||
hasUnreadChat,
|
||||
isChat,
|
||||
isForum,
|
||||
setGroupSection
|
||||
setGroupSection,
|
||||
isPrivate
|
||||
}) => {
|
||||
const [value, setValue] = React.useState(0);
|
||||
return (
|
||||
@ -94,7 +98,20 @@ export const DesktopHeader = ({
|
||||
padding: "10px",
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
gap: '10px'
|
||||
}}>
|
||||
{isPrivate && (
|
||||
<LockIcon sx={{
|
||||
color: 'var(--green)'
|
||||
}} />
|
||||
)}
|
||||
{isPrivate === false && (
|
||||
<NoEncryptionGmailerrorredIcon sx={{
|
||||
color: 'var(--danger)'
|
||||
}} />
|
||||
)}
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "16px",
|
||||
@ -274,6 +291,30 @@ export const DesktopHeader = ({
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setGroupSection("adminSpace");
|
||||
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color={groupSection === 'adminSpace' ? 'black' : "rgba(250, 250, 250, 0.5)"}
|
||||
label="Admins"
|
||||
selected={groupSection === 'adminSpace'}
|
||||
customHeight="55px"
|
||||
selectColor="#09b6e8"
|
||||
>
|
||||
<AdminsIcon
|
||||
height={25}
|
||||
width={20}
|
||||
color={
|
||||
groupSection === 'adminSpace'
|
||||
? "black"
|
||||
: "rgba(250, 250, 250, 0.5)"
|
||||
}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
@ -194,7 +194,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
||||
<AppBar sx={{ position: "relative", bgcolor: "#232428" }}>
|
||||
<Toolbar>
|
||||
<Typography sx={{ ml: 2, flex: 1 }} variant="h6" component="div">
|
||||
Add Group
|
||||
Group Mgmt
|
||||
</Typography>
|
||||
|
||||
<IconButton
|
||||
@ -217,6 +217,8 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
||||
flexGrow: 1,
|
||||
overflowY: "auto",
|
||||
color: "white",
|
||||
flexDirection: 'column',
|
||||
display: 'flex'
|
||||
}}
|
||||
>
|
||||
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
|
||||
@ -454,7 +456,10 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
||||
{value === 1 && (
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
padding: '25px'
|
||||
padding: '25px',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
display: 'flex'
|
||||
}}>
|
||||
<AddGroupList setOpenSnack={setOpenSnack} setInfoSnack={setInfoSnack} />
|
||||
|
||||
@ -465,7 +470,10 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
||||
{value === 2 && (
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
padding: '25px'
|
||||
padding: '25px',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
display: 'flex'
|
||||
}}>
|
||||
<UserListOfInvites myAddress={address} setOpenSnack={setOpenSnack} setInfoSnack={setInfoSnack} />
|
||||
</Box>
|
||||
|
@ -26,6 +26,9 @@ import _ from "lodash";
|
||||
import { MyContext, getBaseApiReact } from "../../App";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import { getBaseApi, getFee } from "../../background";
|
||||
import LockIcon from '@mui/icons-material/Lock';
|
||||
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
|
||||
const cache = new CellMeasurerCache({
|
||||
fixedWidth: true,
|
||||
@ -220,7 +223,17 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
||||
<ListItemButton
|
||||
onClick={(event) => handlePopoverOpen(event, index)}
|
||||
>
|
||||
|
||||
{group?.isOpen === false && (
|
||||
<LockIcon sx={{
|
||||
color: 'var(--green)'
|
||||
}} />
|
||||
)}
|
||||
{group?.isOpen === true && (
|
||||
<NoEncryptionGmailerrorredIcon sx={{
|
||||
color: 'var(--unread)'
|
||||
}} />
|
||||
)}
|
||||
<Spacer width="15px" />
|
||||
<ListItemText
|
||||
primary={group?.groupName}
|
||||
secondary={group?.description}
|
||||
@ -234,7 +247,11 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1
|
||||
}}>
|
||||
<p>Groups list</p>
|
||||
<TextField
|
||||
label="Search for Groups"
|
||||
@ -246,11 +263,10 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
||||
<div
|
||||
style={{
|
||||
position: "relative",
|
||||
height: "500px",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexShrink: 1,
|
||||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
<AutoSizer>
|
||||
@ -267,6 +283,6 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@ -97,6 +97,7 @@ import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmail
|
||||
import { useSetRecoilState } from "recoil";
|
||||
import { selectedGroupIdAtom } from "../../atoms/global";
|
||||
import { sortArrayByTimestampAndGroupName } from "../../utils/time";
|
||||
import { AdminSpace } from "../Chat/AdminSpace";
|
||||
|
||||
// let touchStartY = 0;
|
||||
// let disablePullToRefresh = false;
|
||||
@ -153,6 +154,37 @@ export const getGroupAdminsAddress = async (groupNumber: number) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getPublishesFromAdmins = async (admins: string[], groupId) => {
|
||||
const queryString = admins.map((name) => `name=${name}`).join("&");
|
||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT_PRIVATE&identifier=symmetric-qchat-group-${
|
||||
groupId
|
||||
}&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`;
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error("network error");
|
||||
}
|
||||
const adminData = await response.json();
|
||||
|
||||
const filterId = adminData.filter(
|
||||
(data: any) =>
|
||||
data.identifier === `symmetric-qchat-group-${groupId}`
|
||||
);
|
||||
if (filterId?.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const sortedData = filterId.sort((a: any, b: any) => {
|
||||
// Get the most recent date for both a and b
|
||||
const dateA = a.updated ? new Date(a.updated) : new Date(a.created);
|
||||
const dateB = b.updated ? new Date(b.updated) : new Date(b.created);
|
||||
|
||||
// Sort by most recent
|
||||
return dateB.getTime() - dateA.getTime();
|
||||
});
|
||||
|
||||
|
||||
return sortedData[0];
|
||||
};
|
||||
|
||||
export function validateSecretKey(obj) {
|
||||
// Check if the input is an object
|
||||
if (typeof obj !== "object" || obj === null) {
|
||||
@ -676,9 +708,8 @@ export const Group = ({
|
||||
|
||||
if (
|
||||
group?.data &&
|
||||
isExtMsg(group?.data) &&
|
||||
group?.sender !== myAddress &&
|
||||
group?.timestamp && (!isUpdateMsg(group?.data) || groupChatTimestamps[group?.groupId]) &&
|
||||
group?.timestamp && groupChatTimestamps[group?.groupId] &&
|
||||
((!timestampEnterData[group?.groupId] &&
|
||||
Date.now() - group?.timestamp < timeDifferenceForNotificationChats) ||
|
||||
timestampEnterData[group?.groupId] < group?.timestamp)
|
||||
@ -712,36 +743,7 @@ export const Group = ({
|
||||
// };
|
||||
// }, [checkGroupListFunc, myAddress]);
|
||||
|
||||
const getPublishesFromAdmins = async (admins: string[]) => {
|
||||
// const validApi = await findUsableApi();
|
||||
const queryString = admins.map((name) => `name=${name}`).join("&");
|
||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT_PRIVATE&identifier=symmetric-qchat-group-${
|
||||
selectedGroup?.groupId
|
||||
}&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`;
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error("network error");
|
||||
}
|
||||
const adminData = await response.json();
|
||||
|
||||
const filterId = adminData.filter(
|
||||
(data: any) =>
|
||||
data.identifier === `symmetric-qchat-group-${selectedGroup?.groupId}`
|
||||
);
|
||||
if (filterId?.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const sortedData = filterId.sort((a: any, b: any) => {
|
||||
// Get the most recent date for both a and b
|
||||
const dateA = a.updated ? new Date(a.updated) : new Date(a.created);
|
||||
const dateB = b.updated ? new Date(b.updated) : new Date(b.created);
|
||||
|
||||
// Sort by most recent
|
||||
return dateB.getTime() - dateA.getTime();
|
||||
});
|
||||
|
||||
return sortedData[0];
|
||||
};
|
||||
|
||||
const getSecretKey = async (
|
||||
loadingGroupParam?: boolean,
|
||||
secretKeyToPublish?: boolean
|
||||
@ -768,7 +770,7 @@ export const Group = ({
|
||||
secretKeyToPublish &&
|
||||
secretKey &&
|
||||
lastFetchedSecretKey.current &&
|
||||
Date.now() - lastFetchedSecretKey.current < 1800000
|
||||
Date.now() - lastFetchedSecretKey.current < 600000
|
||||
)
|
||||
return secretKey;
|
||||
if (loadingGroupParam) {
|
||||
@ -790,7 +792,7 @@ export const Group = ({
|
||||
throw new Error("Network error");
|
||||
}
|
||||
const publish =
|
||||
publishFromStorage || (await getPublishesFromAdmins(names));
|
||||
publishFromStorage || (await getPublishesFromAdmins(names, selectedGroup?.groupId));
|
||||
|
||||
if (prevGroupId !== selectedGroupRef.current.groupId) {
|
||||
if (settimeoutForRefetchSecretKey.current) {
|
||||
@ -944,16 +946,14 @@ export const Group = ({
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const getLatestRegularChat = async (groups)=> {
|
||||
try {
|
||||
|
||||
const groupData = {}
|
||||
|
||||
const getGroupData = groups.map(async(group)=> {
|
||||
const isUpdate = isUpdateMsg(group?.data)
|
||||
if(!group.groupId || !group?.timestamp) return null
|
||||
if(isUpdate && (!groupData[group.groupId] || groupData[group.groupId] < group.timestamp)){
|
||||
if((!groupData[group.groupId] || groupData[group.groupId] < group.timestamp)){
|
||||
const hasMoreRecentMsg = await getCountNewMesg(group.groupId, timestampEnterDataRef.current[group?.groupId] || Date.now() - 24 * 60 * 60 * 1000)
|
||||
if(hasMoreRecentMsg){
|
||||
groupData[group.groupId] = hasMoreRecentMsg
|
||||
@ -969,7 +969,6 @@ export const Group = ({
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
@ -1154,9 +1153,9 @@ export const Group = ({
|
||||
.filter((group) => group?.sender !== myAddress)
|
||||
.find((gr) => gr?.groupId === selectedGroup?.groupId);
|
||||
if (!findGroup) return false;
|
||||
if (!findGroup?.data || !isExtMsg(findGroup?.data)) return false;
|
||||
if (!findGroup?.data) return false;
|
||||
return (
|
||||
findGroup?.timestamp && (!isUpdateMsg(findGroup?.data) || groupChatTimestamps[findGroup?.groupId]) &&
|
||||
findGroup?.timestamp && groupChatTimestamps[findGroup?.groupId] &&
|
||||
((!timestampEnterData[selectedGroup?.groupId] &&
|
||||
Date.now() - findGroup?.timestamp <
|
||||
timeDifferenceForNotificationChats) ||
|
||||
@ -2233,7 +2232,7 @@ export const Group = ({
|
||||
/>
|
||||
)}
|
||||
{group?.data &&
|
||||
isExtMsg(group?.data) && (!isUpdateMsg(group?.data) || groupChatTimestamps[group?.groupId]) &&
|
||||
groupChatTimestamps[group?.groupId] &&
|
||||
group?.sender !== myAddress &&
|
||||
group?.timestamp &&
|
||||
((!timestampEnterData[group?.groupId] &&
|
||||
@ -2272,7 +2271,7 @@ export const Group = ({
|
||||
color: "white",
|
||||
}}
|
||||
/>
|
||||
Add Group
|
||||
Group Mgmt
|
||||
</CustomButton>
|
||||
)}
|
||||
{chatMode === "directs" && (
|
||||
@ -2596,7 +2595,7 @@ export const Group = ({
|
||||
style={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
height: "100$",
|
||||
height: "100%",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
padding: "20px",
|
||||
@ -2697,6 +2696,10 @@ export const Group = ({
|
||||
defaultThread={defaultThread}
|
||||
setDefaultThread={setDefaultThread}
|
||||
/>
|
||||
{groupSection === "adminSpace" && (
|
||||
<AdminSpace setIsForceShowCreationKeyPopup={setIsForceShowCreationKeyPopup} adminsWithNames={adminsWithNames} selectedGroup={selectedGroup?.groupId} myAddress={myAddress} userInfo={userInfo} hide={groupSection !== "adminSpace"} isAdmin={admins.includes(myAddress)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -2727,6 +2730,8 @@ export const Group = ({
|
||||
!secretKey &&
|
||||
triedToFetchSecretKey
|
||||
}
|
||||
isForceShowCreationKeyPopup={isForceShowCreationKeyPopup}
|
||||
setIsForceShowCreationKeyPopup={setIsForceShowCreationKeyPopup}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
|
@ -33,6 +33,8 @@ import {
|
||||
import { getNameInfo } from "./Group";
|
||||
import { getBaseApi, getFee } from "../../background";
|
||||
import { LoadingButton } from "@mui/lab";
|
||||
import LockIcon from '@mui/icons-material/Lock';
|
||||
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
||||
import {
|
||||
MyContext,
|
||||
getArbitraryEndpointReact,
|
||||
@ -206,23 +208,25 @@ export const ListOfGroupPromotions = () => {
|
||||
const data = utf8ToBase64(text);
|
||||
const identifier = `group-promotions-ui24-group-${selectedGroup}-${uid.rnd()}`;
|
||||
|
||||
|
||||
await new Promise((res, rej) => {
|
||||
window
|
||||
.sendMessage("publishOnQDN", {
|
||||
data: data,
|
||||
chrome?.runtime?.sendMessage(
|
||||
{
|
||||
action: "publishOnQDN",
|
||||
payload: {
|
||||
data: data,
|
||||
identifier: identifier,
|
||||
service: "DOCUMENT",
|
||||
})
|
||||
.then((response) => {
|
||||
},
|
||||
},
|
||||
(response) => {
|
||||
if (!response?.error) {
|
||||
res(response);
|
||||
return;
|
||||
}
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || "An error occurred");
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
setInfoSnack({
|
||||
type: "success",
|
||||
@ -482,6 +486,31 @@ export const ListOfGroupPromotions = () => {
|
||||
{promotion?.groupName}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Spacer height="20px" />
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
gap: '20px',
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
{promotion?.isOpen === false && (
|
||||
<LockIcon sx={{
|
||||
color: 'var(--green)'
|
||||
}} />
|
||||
)}
|
||||
{promotion?.isOpen === true && (
|
||||
<NoEncryptionGmailerrorredIcon sx={{
|
||||
color: 'var(--unread)'
|
||||
}} />
|
||||
)}
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "15px",
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{promotion?.isOpen ? 'Public group' : 'Private group' }
|
||||
</Typography>
|
||||
</Box>
|
||||
<Spacer height="20px" />
|
||||
<Typography
|
||||
sx={{
|
||||
|
@ -4,7 +4,9 @@ import { AutoSizer, CellMeasurer, CellMeasurerCache, List } from 'react-virtuali
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { getBaseApi, getFee } from '../../background';
|
||||
|
||||
import LockIcon from '@mui/icons-material/Lock';
|
||||
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
|
||||
const cache = new CellMeasurerCache({
|
||||
fixedWidth: true,
|
||||
@ -173,7 +175,17 @@ export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => {
|
||||
</Box>
|
||||
</Popover>
|
||||
<ListItemButton onClick={(event) => handlePopoverOpen(event, index)}>
|
||||
|
||||
{invite?.isOpen === false && (
|
||||
<LockIcon sx={{
|
||||
color: 'var(--green)'
|
||||
}} />
|
||||
)}
|
||||
{invite?.isOpen === true && (
|
||||
<NoEncryptionGmailerrorredIcon sx={{
|
||||
color: 'var(--unread)'
|
||||
}} />
|
||||
)}
|
||||
<Spacer width="15px" />
|
||||
<ListItemText primary={invite?.groupName} secondary={invite?.description} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
@ -184,9 +196,21 @@ export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1
|
||||
}}>
|
||||
<p>Invite list</p>
|
||||
<div style={{ position: 'relative', height: '500px', width: '100%', display: 'flex', flexDirection: 'column', flexShrink: 1 }}>
|
||||
<div
|
||||
style={{
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<List
|
||||
@ -201,6 +225,6 @@ export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => {
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Button, InputAdornment, TextField, TextFieldProps, styled } from "@mui/material";
|
||||
import { useState } from 'react'
|
||||
import { forwardRef, useState } from 'react'
|
||||
|
||||
export const CustomInput = styled(TextField)({
|
||||
width: "183px", // Adjust the width as needed
|
||||
@ -41,7 +41,7 @@ export const CustomInput = styled(TextField)({
|
||||
});
|
||||
|
||||
|
||||
export const PasswordField: React.FunctionComponent<TextFieldProps> = ({ ...props }) => {
|
||||
export const PasswordField = forwardRef<HTMLInputElement, TextFieldProps>( ({ ...props }, ref) => {
|
||||
const [canViewPassword, setCanViewPassword] = useState(false);
|
||||
return (
|
||||
<CustomInput
|
||||
@ -55,7 +55,9 @@ export const PasswordField: React.FunctionComponent<TextFieldProps> = ({ ...prop
|
||||
</InputAdornment>
|
||||
)
|
||||
}}
|
||||
inputRef={ref}
|
||||
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
});
|
@ -255,7 +255,6 @@ const getPublishesFromAdminsAdminSpace = async (
|
||||
return true;
|
||||
});
|
||||
await Promise.all(getMemNames);
|
||||
console.log('members', members)
|
||||
return { names: members, addresses: membersAddresses, both };
|
||||
};
|
||||
|
||||
@ -1778,7 +1777,8 @@ export const getUserWalletFunc = async (coin) => {
|
||||
const wallet = await getSaveWallet();
|
||||
const address = wallet.address0;
|
||||
const resKeyPair = await getKeyPair();
|
||||
const parsedData = resKeyPair;
|
||||
const parsedData = JSON.parse(resKeyPair);
|
||||
|
||||
switch (coin) {
|
||||
case "QORT":
|
||||
userWallet["address"] = address;
|
||||
@ -3179,7 +3179,8 @@ export const signTransaction = async (data, isFromExtension) => {
|
||||
body: data.unsignedBytes,
|
||||
});
|
||||
const resKeyPair = await getKeyPair();
|
||||
const parsedData = resKeyPair;
|
||||
const parsedData = JSON.parse(resKeyPair);
|
||||
|
||||
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
|
||||
const uint8PublicKey = Base58.decode(parsedData.publicKey);
|
||||
const keyPair = {
|
||||
@ -3213,7 +3214,6 @@ export const signTransaction = async (data, isFromExtension) => {
|
||||
|
||||
|
||||
export const decryptQortalGroupData = async (data, sender) => {
|
||||
console.log('data', data)
|
||||
let data64 = data.data64;
|
||||
let groupId = data?.groupId
|
||||
let isAdmins = data?.isAdmins
|
||||
@ -3291,7 +3291,6 @@ url
|
||||
|
||||
|
||||
}
|
||||
console.log('secretKeyObject', secretKeyObject)
|
||||
|
||||
const resGroupDecryptResource = decryptSingle({
|
||||
data64, secretKeyObject: secretKeyObject, skipDecodeBase64: true
|
||||
@ -3320,7 +3319,8 @@ export const encryptDataWithSharingKey = async (data, sender) => {
|
||||
const dataObjectBase64 = await objectToBase64(dataObject)
|
||||
|
||||
const resKeyPair = await getKeyPair();
|
||||
const parsedData = resKeyPair;
|
||||
const parsedData = JSON.parse(resKeyPair);
|
||||
|
||||
const privateKey = parsedData.privateKey;
|
||||
const userPublicKey = parsedData.publicKey;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user