mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-06-14 03:51:23 +00:00
377 lines
11 KiB
TypeScript
377 lines
11 KiB
TypeScript
import { 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';
|
|
import { GroupAvatar } from './GroupAvatar';
|
|
import { useTranslation } from 'react-i18next';
|
|
import i18next from 'i18next';
|
|
|
|
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(i18next.t('core:message.error.network_generic'));
|
|
}
|
|
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,
|
|
balance,
|
|
userInfo,
|
|
isOwner,
|
|
}) => {
|
|
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, setInfoSnackCustom, setOpenSnackGlobal } =
|
|
useContext(MyContext);
|
|
const { t } = useTranslation(['auth', 'core', 'group']);
|
|
|
|
const getAdminGroupSecretKey = useCallback(async () => {
|
|
try {
|
|
if (!selectedGroup) return;
|
|
const getLatestPublish = await getPublishesFromAdminsAdminSpace(
|
|
adminsWithNames.map((admin) => admin?.name),
|
|
selectedGroup
|
|
);
|
|
if (getLatestPublish === false) return;
|
|
|
|
const res = await fetch(
|
|
`${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${
|
|
getLatestPublish.name
|
|
}/${getLatestPublish.identifier}?encoding=base64&rebuild=true`
|
|
);
|
|
|
|
const 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(
|
|
t('auth:message.error.invalid_secret_key', {
|
|
postProcess: 'capitalizeFirst',
|
|
})
|
|
);
|
|
setAdminGroupSecretKey(decryptedKeyToObject);
|
|
setAdminGroupSecretKeyPublishDetails(getLatestPublish);
|
|
} catch (error) {
|
|
console.log(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) {
|
|
console.log(error);
|
|
} finally {
|
|
setIsFetchingGroupSecretKey(false);
|
|
}
|
|
}, [adminsWithNames, selectedGroup]);
|
|
|
|
const createCommonSecretForAdmins = async () => {
|
|
try {
|
|
const fee = await getFee('ARBITRARY');
|
|
|
|
await show({
|
|
message: t('core:message.question.perform_transaction', {
|
|
action: 'ARBITRARY',
|
|
postProcess: 'capitalizeFirst',
|
|
}),
|
|
publishFee: fee.fee + ' QORT',
|
|
});
|
|
|
|
setIsLoadingPublishKey(true);
|
|
|
|
window
|
|
.sendMessage('encryptAndPublishSymmetricKeyGroupChatForAdmins', {
|
|
groupId: selectedGroup,
|
|
previousData: adminGroupSecretKey,
|
|
admins: adminsWithNames,
|
|
})
|
|
.then((response) => {
|
|
if (!response?.error) {
|
|
setInfoSnackCustom({
|
|
type: 'success',
|
|
message: t('auth:message.success.reencrypted_secret_key', {
|
|
postProcess: 'capitalizeFirst',
|
|
}),
|
|
});
|
|
setOpenSnackGlobal(true);
|
|
return;
|
|
}
|
|
setInfoSnackCustom({
|
|
type: 'error',
|
|
message:
|
|
response?.error ||
|
|
t('auth:message.error.unable_reencrypt_secret_key', {
|
|
postProcess: 'capitalizeFirst',
|
|
}),
|
|
});
|
|
setOpenSnackGlobal(true);
|
|
})
|
|
.catch((error) => {
|
|
setInfoSnackCustom({
|
|
type: 'error',
|
|
message:
|
|
error?.message ||
|
|
t('auth:message.error.unable_reencrypt_secret_key', {
|
|
postProcess: 'capitalizeFirst',
|
|
}),
|
|
});
|
|
setOpenSnackGlobal(true);
|
|
});
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
getAdminGroupSecretKey();
|
|
getGroupSecretKey();
|
|
}, [getAdminGroupSecretKey, getGroupSecretKey]);
|
|
return (
|
|
<Box
|
|
sx={{
|
|
alignItems: 'center',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
padding: '10px',
|
|
width: '100%',
|
|
}}
|
|
>
|
|
<Typography
|
|
sx={{
|
|
fontSize: '14px',
|
|
}}
|
|
>
|
|
{t('auth:message.error.publishing_key', {
|
|
postProcess: 'capitalizeFirst',
|
|
})}
|
|
</Typography>
|
|
|
|
<Spacer height="25px" />
|
|
|
|
<Box
|
|
sx={{
|
|
border: '1px solid gray',
|
|
borderRadius: '6px',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: '20px',
|
|
maxWidth: '90%',
|
|
padding: '10px',
|
|
width: '300px',
|
|
}}
|
|
>
|
|
{isFetchingGroupSecretKey && (
|
|
<Typography>
|
|
{t('auth:message.generic.fetching_group_secret_key', {
|
|
postProcess: 'capitalizeFirst',
|
|
})}
|
|
</Typography>
|
|
)}
|
|
|
|
{!isFetchingGroupSecretKey &&
|
|
groupSecretKeyPublishDetails === false && (
|
|
<Typography>
|
|
{t('auth:message.generic.no_secret_key_published', {
|
|
postProcess: 'capitalizeFirst',
|
|
})}
|
|
</Typography>
|
|
)}
|
|
|
|
{groupSecretKeyPublishDetails && (
|
|
<Typography>
|
|
{t('auth:message.generic.last_encryption_date', {
|
|
date: formatTimestampForum(
|
|
groupSecretKeyPublishDetails?.updated ||
|
|
groupSecretKeyPublishDetails?.created
|
|
),
|
|
name: groupSecretKeyPublishDetails?.name,
|
|
postProcess: 'capitalizeFirst',
|
|
})}
|
|
</Typography>
|
|
)}
|
|
|
|
<Button
|
|
disabled={isFetchingGroupSecretKey}
|
|
onClick={() => setIsForceShowCreationKeyPopup(true)}
|
|
variant="contained"
|
|
>
|
|
{t('auth:action.publish_group_secret_key', {
|
|
postProcess: 'capitalizeFirst',
|
|
})}
|
|
</Button>
|
|
|
|
<Spacer height="20px" />
|
|
|
|
<Typography
|
|
sx={{
|
|
fontSize: '14px',
|
|
}}
|
|
>
|
|
{t('auth:tips.key_encrypt_group', {
|
|
postProcess: 'capitalizeFirst',
|
|
})}
|
|
</Typography>
|
|
</Box>
|
|
|
|
<Spacer height="25px" />
|
|
|
|
<Box
|
|
sx={{
|
|
border: '1px solid gray',
|
|
borderRadius: '6px',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: '20px',
|
|
maxWidth: '90%',
|
|
padding: '10px',
|
|
width: '300px',
|
|
}}
|
|
>
|
|
{isFetchingAdminGroupSecretKey && (
|
|
<Typography>
|
|
{t('auth:message.generic.fetching_admin_secret_key', {
|
|
postProcess: 'capitalizeFirst',
|
|
})}
|
|
</Typography>
|
|
)}
|
|
|
|
{!isFetchingAdminGroupSecretKey && !adminGroupSecretKey && (
|
|
<Typography>
|
|
{t('auth:message.generic.no_secret_key_published', {
|
|
postProcess: 'capitalizeFirst',
|
|
})}
|
|
</Typography>
|
|
)}
|
|
|
|
{adminGroupSecretKeyPublishDetails && (
|
|
<Typography>
|
|
{t('auth:message.generic.last_encryption_date', {
|
|
date: formatTimestampForum(
|
|
adminGroupSecretKeyPublishDetails?.updated ||
|
|
adminGroupSecretKeyPublishDetails?.created
|
|
),
|
|
postProcess: 'capitalizeFirst',
|
|
})}
|
|
</Typography>
|
|
)}
|
|
|
|
<Button
|
|
disabled={isFetchingAdminGroupSecretKey}
|
|
onClick={createCommonSecretForAdmins}
|
|
variant="contained"
|
|
>
|
|
{t('auth:action.publish_admin_secret_key', {
|
|
postProcess: 'capitalizeFirst',
|
|
})}
|
|
</Button>
|
|
|
|
<Spacer height="20px" />
|
|
|
|
<Typography
|
|
sx={{
|
|
fontSize: '14px',
|
|
}}
|
|
>
|
|
{t('auth:tips.key_encrypt_admin', {
|
|
postProcess: 'capitalizeFirst',
|
|
})}
|
|
</Typography>
|
|
</Box>
|
|
|
|
<Spacer height="25px" />
|
|
|
|
{isOwner && (
|
|
<Box
|
|
sx={{
|
|
border: '1px solid gray',
|
|
borderRadius: '6px',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
gap: '20px',
|
|
maxWidth: '90%',
|
|
padding: '10px',
|
|
width: '300px',
|
|
alignItems: 'center',
|
|
}}
|
|
>
|
|
<Typography>
|
|
{t('group:group.avatar', {
|
|
postProcess: 'capitalizeFirst',
|
|
})}
|
|
</Typography>
|
|
|
|
<GroupAvatar
|
|
setOpenSnack={setOpenSnackGlobal}
|
|
setInfoSnack={setInfoSnackCustom}
|
|
myName={userInfo?.name}
|
|
balance={balance}
|
|
groupId={selectedGroup}
|
|
/>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
);
|
|
};
|