Add style, remove dialog (useless)

This commit is contained in:
Nicola Benaglia 2025-04-20 10:10:51 +02:00
parent 88f4ce4e4d
commit 1797754283

View File

@ -1,6 +1,5 @@
import React, { useContext, useMemo, useState } from "react"; import React, { useContext, useMemo, useState } from 'react';
import { import {
Avatar,
Box, Box,
Button, Button,
ButtonBase, ButtonBase,
@ -13,14 +12,17 @@ import {
Select, Select,
Tab, Tab,
Tabs, Tabs,
Typography, useTheme,
} from "@mui/material"; } from '@mui/material';
import { useDropzone } from "react-dropzone"; import { useDropzone } from 'react-dropzone';
import { useHandlePrivateApps } from "./useHandlePrivateApps"; import { useHandlePrivateApps } from './useHandlePrivateApps';
import { useRecoilState, useSetRecoilState } from "recoil"; import { useRecoilState } from 'recoil';
import { groupsPropertiesAtom, myGroupsWhereIAmAdminAtom } from "../../atoms/global"; import {
import { Label } from "../Group/AddGroup"; groupsPropertiesAtom,
import { Spacer } from "../../common/Spacer"; myGroupsWhereIAmAdminAtom,
} from '../../atoms/global';
import { Label } from '../Group/AddGroup';
import { Spacer } from '../../common/Spacer';
import { import {
Add, Add,
AppCircle, AppCircle,
@ -28,52 +30,59 @@ import {
AppCircleLabel, AppCircleLabel,
PublishQAppChoseFile, PublishQAppChoseFile,
PublishQAppInfo, PublishQAppInfo,
} from "./Apps-styles"; } from './Apps-styles';
import ImageUploader from "../../common/ImageUploader"; import ImageUploader from '../../common/ImageUploader';
import { isMobile, MyContext } from "../../App"; import { isMobile, MyContext } from '../../App';
import { fileToBase64 } from "../../utils/fileReading"; import { fileToBase64 } from '../../utils/fileReading';
import { objectToBase64 } from "../../qdn/encryption/group-encryption"; import { objectToBase64 } from '../../qdn/encryption/group-encryption';
import { getFee } from "../../background"; import { getFee } from '../../background';
const maxFileSize = 50 * 1024 * 1024; // 50MB const maxFileSize = 50 * 1024 * 1024; // 50MB
export const AppsPrivate = ({myName}) => { export const AppsPrivate = ({ myName }) => {
const { openApp } = useHandlePrivateApps(); const { openApp } = useHandlePrivateApps();
const [file, setFile] = useState(null); const [file, setFile] = useState(null);
const [logo, setLogo] = useState(null); const [logo, setLogo] = useState(null);
const [qortalUrl, setQortalUrl] = useState(""); const [qortalUrl, setQortalUrl] = useState('');
const [selectedGroup, setSelectedGroup] = useState(0); const [selectedGroup, setSelectedGroup] = useState(0);
const [groupsProperties] = useRecoilState(groupsPropertiesAtom) const [groupsProperties] = useRecoilState(groupsPropertiesAtom);
const [valueTabPrivateApp, setValueTabPrivateApp] = useState(0); const [valueTabPrivateApp, setValueTabPrivateApp] = useState(0);
const [myGroupsWhereIAmAdminFromGlobal] = useRecoilState( const [myGroupsWhereIAmAdminFromGlobal] = useRecoilState(
myGroupsWhereIAmAdminAtom myGroupsWhereIAmAdminAtom
); );
const myGroupsWhereIAmAdmin = useMemo(()=> { const myGroupsWhereIAmAdmin = useMemo(() => {
return myGroupsWhereIAmAdminFromGlobal?.filter((group)=> groupsProperties[group?.groupId]?.isOpen === false) return myGroupsWhereIAmAdminFromGlobal?.filter(
}, [myGroupsWhereIAmAdminFromGlobal, groupsProperties]) (group) => groupsProperties[group?.groupId]?.isOpen === false
);
}, [myGroupsWhereIAmAdminFromGlobal, groupsProperties]);
const [isOpenPrivateModal, setIsOpenPrivateModal] = useState(false); const [isOpenPrivateModal, setIsOpenPrivateModal] = useState(false);
const { show, setInfoSnackCustom, setOpenSnackGlobal, memberGroups } = useContext(MyContext); const { show, setInfoSnackCustom, setOpenSnackGlobal, memberGroups } =
useContext(MyContext);
const theme = useTheme();
const myGroupsPrivate = useMemo(() => {
const myGroupsPrivate = useMemo(()=> { return memberGroups?.filter(
return memberGroups?.filter((group)=> groupsProperties[group?.groupId]?.isOpen === false) (group) => groupsProperties[group?.groupId]?.isOpen === false
}, [memberGroups, groupsProperties]) );
}, [memberGroups, groupsProperties]);
const [privateAppValues, setPrivateAppValues] = useState({ const [privateAppValues, setPrivateAppValues] = useState({
name: "", name: '',
service: "DOCUMENT", service: 'DOCUMENT',
identifier: "", identifier: '',
groupId: 0, groupId: 0,
}); });
const [newPrivateAppValues, setNewPrivateAppValues] = useState({ const [newPrivateAppValues, setNewPrivateAppValues] = useState({
service: "DOCUMENT", service: 'DOCUMENT',
identifier: "", identifier: '',
name: "", name: '',
}); });
const { getRootProps, getInputProps } = useDropzone({ const { getRootProps, getInputProps } = useDropzone({
accept: { accept: {
"application/zip": [".zip"], // Only accept zip files 'application/zip': ['.zip'], // Only accept zip files
}, },
maxSize: maxFileSize, maxSize: maxFileSize,
multiple: false, // Disable multiple file uploads multiple: false, // Disable multiple file uploads
@ -85,7 +94,7 @@ export const AppsPrivate = ({myName}) => {
onDropRejected: (fileRejections) => { onDropRejected: (fileRejections) => {
fileRejections.forEach(({ file, errors }) => { fileRejections.forEach(({ file, errors }) => {
errors.forEach((error) => { errors.forEach((error) => {
if (error.code === "file-too-large") { if (error.code === 'file-too-large') {
console.error( console.error(
`File ${file.name} is too large. Max size allowed is ${ `File ${file.name} is too large. Max size allowed is ${
maxFileSize / (1024 * 1024) maxFileSize / (1024 * 1024)
@ -101,24 +110,23 @@ export const AppsPrivate = ({myName}) => {
try { try {
if (privateAppValues?.groupId === 0) return; if (privateAppValues?.groupId === 0) return;
await openApp(privateAppValues, true); await openApp(privateAppValues, true);
} catch (error) { } catch (error) {
console.error(error) console.error(error);
} }
}; };
const clearFields = () => { const clearFields = () => {
setPrivateAppValues({ setPrivateAppValues({
name: "", name: '',
service: "DOCUMENT", service: 'DOCUMENT',
identifier: "", identifier: '',
groupId: 0, groupId: 0,
}); });
setNewPrivateAppValues({ setNewPrivateAppValues({
service: "DOCUMENT", service: 'DOCUMENT',
identifier: "", identifier: '',
name: "", name: '',
}); });
setFile(null); setFile(null);
setValueTabPrivateApp(0); setValueTabPrivateApp(0);
@ -129,9 +137,9 @@ export const AppsPrivate = ({myName}) => {
const publishPrivateApp = async () => { const publishPrivateApp = async () => {
try { try {
if (selectedGroup === 0) return; if (selectedGroup === 0) return;
if (!logo) throw new Error("Please select an image for a logo"); if (!logo) throw new Error('Please select an image for a logo');
if (!myName) throw new Error("You need a Qortal name to publish"); if (!myName) throw new Error('You need a Qortal name to publish');
if (!newPrivateAppValues?.name) throw new Error("Your app needs a name"); if (!newPrivateAppValues?.name) throw new Error('Your app needs a name');
const base64Logo = await fileToBase64(logo); const base64Logo = await fileToBase64(logo);
const base64App = await fileToBase64(file); const base64App = await fileToBase64(file);
const objectToSave = { const objectToSave = {
@ -141,27 +149,29 @@ export const AppsPrivate = ({myName}) => {
}; };
const object64 = await objectToBase64(objectToSave); const object64 = await objectToBase64(objectToSave);
const decryptedData = await window.sendMessage( const decryptedData = await window.sendMessage(
"ENCRYPT_QORTAL_GROUP_DATA", 'ENCRYPT_QORTAL_GROUP_DATA',
{ {
base64: object64, base64: object64,
groupId: selectedGroup, groupId: selectedGroup,
} }
); );
if (decryptedData?.error) { if (decryptedData?.error) {
throw new Error( throw new Error(
decryptedData?.error || "Unable to encrypt app. App not published" decryptedData?.error || 'Unable to encrypt app. App not published'
); );
} }
const fee = await getFee("ARBITRARY");
const fee = await getFee('ARBITRARY');
await show({ await show({
message: "Would you like to publish this app?", message: 'Would you like to publish this app?',
publishFee: fee.fee + " QORT", publishFee: fee.fee + ' QORT',
}); });
await new Promise((res, rej) => { await new Promise((res, rej) => {
window window
.sendMessage("publishOnQDN", { .sendMessage('publishOnQDN', {
data: decryptedData, data: decryptedData,
identifier: newPrivateAppValues?.identifier, identifier: newPrivateAppValues?.identifier,
service: newPrivateAppValues?.service, service: newPrivateAppValues?.service,
@ -174,9 +184,10 @@ export const AppsPrivate = ({myName}) => {
rej(response.error); rej(response.error);
}) })
.catch((error) => { .catch((error) => {
rej(error.message || "An error occurred"); rej(error.message || 'An error occurred');
}); });
}); });
openApp( openApp(
{ {
identifier: newPrivateAppValues?.identifier, identifier: newPrivateAppValues?.identifier,
@ -188,10 +199,10 @@ export const AppsPrivate = ({myName}) => {
); );
clearFields(); clearFields();
} catch (error) { } catch (error) {
setOpenSnackGlobal(true) setOpenSnackGlobal(true);
setInfoSnackCustom({ setInfoSnackCustom({
type: "error", type: 'error',
message: error?.message || "Unable to publish app", message: error?.message || 'Unable to publish app',
}); });
} }
}; };
@ -203,9 +214,10 @@ export const AppsPrivate = ({myName}) => {
function a11yProps(index: number) { function a11yProps(index: number) {
return { return {
id: `simple-tab-${index}`, id: `simple-tab-${index}`,
"aria-controls": `simple-tabpanel-${index}`, 'aria-controls': `simple-tabpanel-${index}`,
}; };
} }
return ( return (
<> <>
<ButtonBase <ButtonBase
@ -213,17 +225,18 @@ export const AppsPrivate = ({myName}) => {
setIsOpenPrivateModal(true); setIsOpenPrivateModal(true);
}} }}
sx={{ sx={{
width: "80px", width: '80px',
}} }}
> >
<AppCircleContainer <AppCircleContainer
sx={{ sx={{
gap: !isMobile ? "10px" : "5px", gap: !isMobile ? '10px' : '5px',
}} }}
> >
<AppCircle> <AppCircle>
<Add>+</Add> <Add>+</Add>
</AppCircle> </AppCircle>
<AppCircleLabel>Private</AppCircleLabel> <AppCircleLabel>Private</AppCircleLabel>
</AppCircleContainer> </AppCircleContainer>
</ButtonBase> </ButtonBase>
@ -233,7 +246,7 @@ export const AppsPrivate = ({myName}) => {
aria-labelledby="alert-dialog-title" aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description" aria-describedby="alert-dialog-description"
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === "Enter") { if (e.key === 'Enter') {
if (valueTabPrivateApp === 0) { if (valueTabPrivateApp === 0) {
if ( if (
!privateAppValues.name || !privateAppValues.name ||
@ -249,23 +262,17 @@ export const AppsPrivate = ({myName}) => {
maxWidth="md" maxWidth="md"
fullWidth={true} fullWidth={true}
> >
<DialogTitle id="alert-dialog-title">
{valueTabPrivateApp === 0
? "Access private app"
: "Publish private app"}
</DialogTitle>
<Box> <Box>
<Tabs <Tabs
value={valueTabPrivateApp} value={valueTabPrivateApp}
onChange={handleChange} onChange={handleChange}
aria-label="basic tabs example" aria-label="basic tabs example"
variant={isMobile ? "scrollable" : "fullWidth"} // Scrollable on mobile, full width on desktop variant={isMobile ? 'scrollable' : 'fullWidth'} // Scrollable on mobile, full width on desktop
scrollButtons="auto" scrollButtons="auto"
allowScrollButtonsMobile allowScrollButtonsMobile
sx={{ sx={{
"& .MuiTabs-indicator": { '& .MuiTabs-indicator': {
backgroundColor: "white", backgroundColor: theme.palette.background.default,
}, },
}} }}
> >
@ -273,20 +280,20 @@ export const AppsPrivate = ({myName}) => {
label="Access app" label="Access app"
{...a11yProps(0)} {...a11yProps(0)}
sx={{ sx={{
"&.Mui-selected": { '&.Mui-selected': {
color: "white", color: theme.palette.text.primary,
}, },
fontSize: isMobile ? "0.75rem" : "1rem", // Adjust font size for mobile fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
}} }}
/> />
<Tab <Tab
label="Publish app" label="Publish app"
{...a11yProps(1)} {...a11yProps(1)}
sx={{ sx={{
"&.Mui-selected": { '&.Mui-selected': {
color: "white", color: theme.palette.text.primary,
}, },
fontSize: isMobile ? "0.75rem" : "1rem", // Adjust font size for mobile fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
}} }}
/> />
</Tabs> </Tabs>
@ -296,9 +303,9 @@ export const AppsPrivate = ({myName}) => {
<DialogContent> <DialogContent>
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
gap: "5px", gap: '5px',
}} }}
> >
<Label>Select a group</Label> <Label>Select a group</Label>
@ -333,10 +340,10 @@ export const AppsPrivate = ({myName}) => {
<Spacer height="10px" /> <Spacer height="10px" />
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
gap: "5px", gap: '5px',
marginTop: "15px", marginTop: '15px',
}} }}
> >
<Label>name</Label> <Label>name</Label>
@ -355,10 +362,10 @@ export const AppsPrivate = ({myName}) => {
</Box> </Box>
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
gap: "5px", gap: '5px',
marginTop: "15px", marginTop: '15px',
}} }}
> >
<Label>identifier</Label> <Label>identifier</Label>
@ -376,6 +383,7 @@ export const AppsPrivate = ({myName}) => {
/> />
</Box> </Box>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button <Button
variant="contained" variant="contained"
@ -406,15 +414,19 @@ export const AppsPrivate = ({myName}) => {
<DialogContent> <DialogContent>
<PublishQAppInfo <PublishQAppInfo
sx={{ sx={{
fontSize: "14px", backgroundColor: theme.palette.background.paper,
fontSize: '14px',
}} }}
> >
Select .zip file containing static content:{" "} Select .zip file containing static content:{' '}
</PublishQAppInfo> </PublishQAppInfo>
<Spacer height="10px" /> <Spacer height="10px" />
<PublishQAppInfo <PublishQAppInfo
sx={{ sx={{
fontSize: "14px", backgroundColor: theme.palette.background.paper,
fontSize: '14px',
}} }}
>{` >{`
50mb MB maximum`}</PublishQAppInfo> 50mb MB maximum`}</PublishQAppInfo>
@ -426,17 +438,26 @@ export const AppsPrivate = ({myName}) => {
)} )}
<Spacer height="18px" /> <Spacer height="18px" />
<PublishQAppChoseFile {...getRootProps()}>
{" "} <PublishQAppChoseFile
sx={{
backgroundColor: theme.palette.background.default,
fontSize: '14px',
}}
{...getRootProps()}
>
{' '}
<input {...getInputProps()} /> <input {...getInputProps()} />
{file ? "Change" : "Choose"} File {file ? 'Change' : 'Choose'} File
</PublishQAppChoseFile> </PublishQAppChoseFile>
<Spacer height="20px" /> <Spacer height="20px" />
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
gap: "5px", gap: '5px',
}} }}
> >
<Label>Select a group</Label> <Label>Select a group</Label>
@ -462,14 +483,15 @@ export const AppsPrivate = ({myName}) => {
})} })}
</Select> </Select>
</Box> </Box>
<Spacer height="20px" /> <Spacer height="20px" />
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
gap: "5px", gap: '5px',
marginTop: "15px", marginTop: '15px',
}} }}
> >
<Label>identifier</Label> <Label>identifier</Label>
@ -486,13 +508,15 @@ export const AppsPrivate = ({myName}) => {
} }
/> />
</Box> </Box>
<Spacer height="10px" /> <Spacer height="10px" />
<Box <Box
sx={{ sx={{
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
gap: "5px", gap: '5px',
marginTop: "15px", marginTop: '15px',
}} }}
> >
<Label>App name</Label> <Label>App name</Label>
@ -511,12 +535,15 @@ export const AppsPrivate = ({myName}) => {
</Box> </Box>
<Spacer height="10px" /> <Spacer height="10px" />
<ImageUploader onPick={(file) => setLogo(file)}> <ImageUploader onPick={(file) => setLogo(file)}>
<Button variant="contained">Choose logo</Button> <Button variant="contained">Choose logo</Button>
</ImageUploader> </ImageUploader>
{logo?.name} {logo?.name}
<Spacer height="25px" /> <Spacer height="25px" />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button <Button
variant="contained" variant="contained"
@ -527,6 +554,7 @@ export const AppsPrivate = ({myName}) => {
> >
Close Close
</Button> </Button>
<Button <Button
disabled={ disabled={
!newPrivateAppValues.name || !newPrivateAppValues.name ||