import React, {
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
import {
Box,
Button,
ButtonBase,
Dialog,
DialogActions,
DialogContent,
Input,
MenuItem,
Select,
Tab,
Tabs,
useTheme,
} from '@mui/material';
import { useDropzone } from 'react-dropzone';
import { useHandlePrivateApps } from '../../hooks/useHandlePrivateApps';
import {
groupsPropertiesAtom,
memberGroupsAtom,
myGroupsWhereIAmAdminAtom,
} from '../../atoms/global';
import { Label } from '../Group/AddGroup';
import { Spacer } from '../../common/Spacer';
import {
AppCircle,
AppCircleContainer,
AppCircleLabel,
PublishQAppChoseFile,
PublishQAppInfo,
} from './Apps-styles';
import AddIcon from '@mui/icons-material/Add';
import ImageUploader from '../../common/ImageUploader';
import { QORTAL_APP_CONTEXT } from '../../App';
import { fileToBase64 } from '../../utils/fileReading';
import { objectToBase64 } from '../../qdn/encryption/group-encryption';
import { getFee } from '../../background/background.ts';
import { useAtom } from 'jotai';
import { useTranslation } from 'react-i18next';
import { useSortedMyNames } from '../../hooks/useSortedMyNames';
const maxFileSize = 50 * 1024 * 1024; // 50MB
export const AppsPrivate = ({ myName, myAddress }) => {
const [names, setNames] = useState([]);
const [name, setName] = useState(0);
const { openApp } = useHandlePrivateApps();
const [file, setFile] = useState(null);
const [logo, setLogo] = useState(null);
const [qortalUrl, setQortalUrl] = useState('');
const [selectedGroup, setSelectedGroup] = useState(0);
const [valueTabPrivateApp, setValueTabPrivateApp] = useState(0);
const [groupsProperties] = useAtom(groupsPropertiesAtom);
const [myGroupsWhereIAmAdminFromGlobal] = useAtom(myGroupsWhereIAmAdminAtom);
const myGroupsWhereIAmAdmin = useMemo(() => {
return myGroupsWhereIAmAdminFromGlobal?.filter(
(group) => groupsProperties[group?.groupId]?.isOpen === false
);
}, [myGroupsWhereIAmAdminFromGlobal, groupsProperties]);
const [isOpenPrivateModal, setIsOpenPrivateModal] = useState(false);
const { show, setInfoSnackCustom, setOpenSnackGlobal } =
useContext(QORTAL_APP_CONTEXT);
const [memberGroups] = useAtom(memberGroupsAtom);
const theme = useTheme();
const { t } = useTranslation(['auth', 'core', 'group']);
const myGroupsPrivate = useMemo(() => {
return memberGroups?.filter(
(group) => groupsProperties[group?.groupId]?.isOpen === false
);
}, [memberGroups, groupsProperties]);
const [privateAppValues, setPrivateAppValues] = useState({
name: '',
service: 'DOCUMENT',
identifier: '',
groupId: 0,
});
const [newPrivateAppValues, setNewPrivateAppValues] = useState({
service: 'DOCUMENT',
identifier: '',
name: '',
});
const mySortedNames = useSortedMyNames(names, myName);
const { getRootProps, getInputProps } = useDropzone({
accept: {
'application/zip': ['.zip'], // Only accept zip files
},
maxSize: maxFileSize,
multiple: false, // Disable multiple file uploads
onDrop: (acceptedFiles) => {
if (acceptedFiles.length > 0) {
setFile(acceptedFiles[0]); // Set the file name
}
},
onDropRejected: (fileRejections) => {
fileRejections.forEach(({ file, errors }) => {
errors.forEach((error) => {
if (error.code === 'file-too-large') {
console.error(
t('core:message.error.file_too_large', {
filename: file.name,
size: maxFileSize / (1024 * 1024),
postProcess: 'capitalizeFirstChar',
})
);
}
});
});
},
});
const addPrivateApp = async () => {
try {
if (privateAppValues?.groupId === 0) return;
await openApp(privateAppValues, true);
} catch (error) {
console.error(error);
}
};
const clearFields = () => {
setPrivateAppValues({
name: '',
service: 'DOCUMENT',
identifier: '',
groupId: 0,
});
setNewPrivateAppValues({
service: 'DOCUMENT',
identifier: '',
name: '',
});
setFile(null);
setValueTabPrivateApp(0);
setSelectedGroup(0);
setLogo(null);
};
const publishPrivateApp = async () => {
try {
if (selectedGroup === 0) return;
if (!logo)
throw new Error(
t('core:message.generic.select_image', {
postProcess: 'capitalizeFirstChar',
})
);
if (!myName)
throw new Error(
t('core:message.generic.name_publish', {
postProcess: 'capitalizeFirstChar',
})
);
if (!newPrivateAppValues?.name)
throw new Error(
t('core:message.error.app_need_name', {
postProcess: 'capitalizeFirstChar',
})
);
const base64Logo = await fileToBase64(logo);
const base64App = await fileToBase64(file);
const objectToSave = {
app: base64App,
logo: base64Logo,
name: newPrivateAppValues.name,
};
const object64 = await objectToBase64(objectToSave);
const decryptedData = await window.sendMessage(
'ENCRYPT_QORTAL_GROUP_DATA',
{
base64: object64,
groupId: selectedGroup,
}
);
if (decryptedData?.error) {
throw new Error(
decryptedData?.error ||
t('core:message.error.encrypt_app', {
postProcess: 'capitalizeFirstChar',
})
);
}
const fee = await getFee('ARBITRARY');
await show({
message: t('core:message.question.publish_app', {
postProcess: 'capitalizeFirstChar',
}),
publishFee: fee.fee + ' QORT',
});
await new Promise((res, rej) => {
window
.sendMessage('publishOnQDN', {
data: decryptedData,
identifier: newPrivateAppValues?.identifier,
service: newPrivateAppValues?.service,
uploadType: 'base64',
name,
})
.then((response) => {
if (!response?.error) {
res(response);
return;
}
rej(response.error);
})
.catch((error) => {
rej(
error.message ||
t('core:message.error.generic', {
postProcess: 'capitalizeFirstChar',
})
);
});
});
openApp(
{
identifier: newPrivateAppValues?.identifier,
service: newPrivateAppValues?.service,
name,
groupId: selectedGroup,
},
true
);
clearFields();
} catch (error) {
setOpenSnackGlobal(true);
setInfoSnackCustom({
type: 'error',
message:
error?.message ||
t('core:message.error.publish_app', {
postProcess: 'capitalizeFirstChar',
}),
});
}
};
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
setValueTabPrivateApp(newValue);
};
function a11yProps(index: number) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
const getNames = useCallback(async () => {
if (!myAddress) return;
try {
const res = await fetch(
`${getBaseApiReact()}/names/address/${myAddress}?limit=0`
);
const data = await res.json();
setNames(data?.map((item) => item.name));
} catch (error) {
console.error(error);
}
}, [myAddress]);
useEffect(() => {
getNames();
}, [getNames]);
return (
<>
{
setIsOpenPrivateModal(true);
}}
sx={{
width: '80px',
}}
>
Private
{isOpenPrivateModal && (
)}
>
);
};