@mui/material#DialogContentText TypeScript Examples
The following examples show how to use
@mui/material#DialogContentText.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: DeleteAccessRule.tsx From console with GNU Affero General Public License v3.0 | 6 votes |
DeleteAccessRule = ({
onClose,
modalOpen,
bucket,
toDelete,
}: IDeleteAccessRule) => {
const dispatch = useDispatch();
const onDelSuccess = () => onClose();
const onDelError = (err: ErrorResponseHandler) =>
dispatch(setErrorSnackMessage(err));
const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError);
const onConfirmDelete = () => {
invokeDeleteApi("DELETE", `/api/v1/bucket/${bucket}/access-rules`, {
prefix: toDelete,
});
};
return (
<ConfirmDialog
title={`Delete Access Rule`}
confirmText={"Delete"}
isOpen={modalOpen}
isLoading={deleteLoading}
onConfirm={onConfirmDelete}
titleIcon={<ConfirmDeleteIcon />}
onClose={onClose}
confirmationContent={
<DialogContentText>
Are you sure you want to delete this access rule?
</DialogContentText>
}
/>
);
}
Example #2
Source File: cookies-dialog.tsx From master-frontend-lemoncode with MIT License | 6 votes |
CookiesDialog: React.FunctionComponent<Props> = (props) => {
const { onAgreeClick } = props;
const [open, setOpen] = React.useState(false);
const handleAgreeClick = () => {
setOpen(false);
onAgreeClick();
};
return (
<>
<Button variant="outlined" onClick={() => setOpen(true)}>
Learn more about our cookies
</Button>
<Dialog open={open} onClose={() => setOpen(false)}>
<DialogTitle>About cookies</DialogTitle>
<DialogContent>
<DialogContentText>
Any information that you voluntarily provide to us, including your
name and email address, will be used for the sole purpose for which
the information was provided to us. In addition, communication
exchanges on this website are public (not private) communications.
Therefore, any message that you post on this website will be
considered and treated as available for public use and distribution.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button color="primary" onClick={handleAgreeClick}>
Agree
</Button>
</DialogActions>
</Dialog>
</>
);
}
Example #3
Source File: UpdateRequiredDialog.tsx From airmessage-web with Apache License 2.0 | 6 votes |
/**
* A dialog that warns the user to check their server for updates
*/
export default function UpdateRequiredDialog(props: {isOpen: boolean, onDismiss: () => void}) {
return (
<Dialog
open={props.isOpen}
onClose={props.onDismiss}
fullWidth>
<DialogTitle>Your server needs to be updated</DialogTitle>
<DialogContent>
<DialogContentText>
<Typography paragraph>
You're running an unsupported version of AirMessage Server.
</Typography>
<Typography paragraph>
Unsupported versions of AirMessage Server may contain security or stability issues,
and will start refusing connections late January.
</Typography>
<Typography paragraph>
Please install the latest version of AirMessage Server from <Link href="https://airmessage.org" target="_blank">airmessage.org</Link> on your Mac.
</Typography>
</DialogContentText>
</DialogContent>
</Dialog>
);
}
Example #4
Source File: GroupDeleteModal.tsx From abrechnung with GNU Affero General Public License v3.0 | 6 votes |
export default function GroupDeleteModal({ show, onClose, groupToDelete }) {
const confirmDeleteGroup = () => {
deleteGroup({ groupID: groupToDelete.id })
.then((res) => {
onClose();
})
.catch((err) => {
toast.error(err);
});
};
return (
<Dialog open={show} onClose={onClose}>
<DialogTitle>Delete Group</DialogTitle>
<DialogContent>
<DialogContentText>
{groupToDelete ? <span>Are you sure you want to delete group {groupToDelete.name}</span> : null}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button color="primary" onClick={onClose}>
No
</Button>
<Button color="error" onClick={confirmDeleteGroup}>
Yes pls
</Button>
</DialogActions>
</Dialog>
);
}
Example #5
Source File: Files.tsx From NekoMaid with MIT License | 6 votes |
CompressDialog: React.FC<{ file: string | null, dirs: Record<string, boolean>, onClose: () => void, plugin: Plugin, path: string, refresh: () => void }> =
({ dirs, file, onClose, plugin, path, refresh }) => {
const [value, setValue] = useState('')
const [ext, setExt] = useState('zip')
useEffect(() => {
setValue(file || 'server')
}, [file])
let error: string | undefined
if (!validFilename(value)) error = lang.files.wrongName
else if (((path || '/') + value + '.' + ext) in dirs) error = lang.files.exists
return <Dialog open={file != null} onClose={onClose}>
<DialogTitle>{lang.files.compress}</DialogTitle>
<DialogContent>
<DialogContentText>{lang.files.compressName}</DialogContentText>
<TextField value={value} variant='standard' error={!!error} helperText={error} onChange={e => setValue(e.target.value)} />
<Select variant='standard' value={ext} onChange={e => setExt(e.target.value)}>
{compressFileExt.map(it => <MenuItem key={it} value={it}>.{it}</MenuItem>)}
</Select>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>{minecraft['gui.cancel']}</Button>
<Button disabled={!!error} onClick={() => {
onClose()
plugin.emit('files:compress', (res: boolean) => {
action(res)
refresh()
}, file, value, ext)
}}>{minecraft['gui.ok']}</Button>
</DialogActions>
</Dialog>
}
Example #6
Source File: DeleteMultipleServiceAccounts.tsx From console with GNU Affero General Public License v3.0 | 6 votes |
DeleteMultipleSAs = ({
closeDeleteModalAndRefresh,
deleteOpen,
selectedSAs,
}: IDeleteMultiSAsProps) => {
const dispatch = useDispatch();
const onDelSuccess = () => closeDeleteModalAndRefresh(true);
const onDelError = (err: ErrorResponseHandler) =>
dispatch(setErrorSnackMessage(err));
const onClose = () => closeDeleteModalAndRefresh(false);
const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError);
if (!selectedSAs) {
return null;
}
const onConfirmDelete = () => {
invokeDeleteApi(
"DELETE",
`/api/v1/service-accounts/delete-multi`,
selectedSAs
);
};
return (
<ConfirmDialog
title={`Delete Service Accounts`}
confirmText={"Delete"}
isOpen={deleteOpen}
titleIcon={<ConfirmDeleteIcon />}
isLoading={deleteLoading}
onConfirm={onConfirmDelete}
onClose={onClose}
confirmationContent={
<DialogContentText>
Are you sure you want to delete the selected {selectedSAs.length}{" "}
service accounts?{" "}
</DialogContentText>
}
/>
);
}
Example #7
Source File: FaceTimeLinkDialog.tsx From airmessage-web with Apache License 2.0 | 6 votes |
export default function FaceTimeLinkDialog(props: {
isOpen: boolean,
onDismiss: () => void,
link: string
}) {
const propsLink = props.link;
const propsOnDismiss = props.onDismiss;
const copyLink = useCallback(async () => {
await navigator.clipboard.writeText(propsLink);
propsOnDismiss();
}, [propsLink, propsOnDismiss]);
return (
<Dialog
open={props.isOpen}
onClose={props.onDismiss}>
<DialogTitle>FaceTime link</DialogTitle>
<DialogContent>
<DialogContentText>
{props.link}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={props.onDismiss} color="primary">
Cancel
</Button>
<Button onClick={copyLink} color="primary" autoFocus>
Copy
</Button>
</DialogActions>
</Dialog>
);
}
Example #8
Source File: ResetConfigurationModal.tsx From console with GNU Affero General Public License v3.0 | 5 votes |
ResetConfigurationModal = ({
classes,
configurationName,
closeResetModalAndRefresh,
resetOpen,
}: IResetConfiguration) => {
const dispatch = useDispatch();
const [resetLoading, setResetLoading] = useState<boolean>(false);
useEffect(() => {
if (resetLoading) {
api
.invoke("POST", `/api/v1/configs/${configurationName}/reset`)
.then((res) => {
setResetLoading(false);
closeResetModalAndRefresh(true);
})
.catch((err: ErrorResponseHandler) => {
setResetLoading(false);
dispatch(setErrorSnackMessage(err));
});
}
}, [closeResetModalAndRefresh, configurationName, resetLoading, dispatch]);
const resetConfiguration = () => {
setResetLoading(true);
};
return (
<ConfirmDialog
title={`Restore Defaults`}
confirmText={"Yes, Reset Configuration"}
isOpen={resetOpen}
titleIcon={<ConfirmDeleteIcon />}
isLoading={resetLoading}
onConfirm={resetConfiguration}
onClose={() => {
closeResetModalAndRefresh(false);
}}
confirmationContent={
<React.Fragment>
{resetLoading && <LinearProgress />}
<DialogContentText>
Are you sure you want to restore these configurations to default
values?
<br />
<b className={classes.wrapText}>
Please note that this may cause your system to not be accessible
</b>
</DialogContentText>
</React.Fragment>
}
/>
);
}
Example #9
Source File: ServerSwitch.tsx From NekoMaid with MIT License | 5 votes |
ServerSwitch: React.FC<DialogProps> = props => {
const [value, setValue] = useState<string>('')
let error = false
// eslint-disable-next-line no-new
try { if (value) new URL(value.startsWith('http://') ? value : 'http://' + value) } catch { error = true }
return <Dialog fullWidth maxWidth='xs' {...props}>
<DialogTitle>{lang.serverSwitch.title}</DialogTitle>
<DialogContent sx={{ overflow: 'hidden' }}>
<Autocomplete
freeSolo
inputValue={value}
clearOnBlur={false}
onInputChange={(_: any, v: string) => setValue(v)}
noOptionsText={lang.serverSwitch.noServer}
style={{ width: '100%', maxWidth: 500, marginTop: 10 }}
options={JSON.parse(localStorage.getItem('NekoMaid:servers') || '[]')}
getOptionLabel={(option: any) => option.address}
renderInput={(props: any) => <TextField
{...props}
error={error}
label={minecraft['addServer.enterIp']}
helperText={error ? lang.serverSwitch.wrongHostname : undefined}
/>}
/>
<DialogContentText>{lang.serverSwitch.content}</DialogContentText>
</DialogContent>
<DialogActions>
<Button
color='primary'
disabled={error}
onClick={() => (location.search = '?' + encodeURIComponent(value))}
>{lang.serverSwitch.connect}</Button>
</DialogActions>
</Dialog>
}
Example #10
Source File: NewNickname.tsx From sapio-studio with Mozilla Public License 2.0 | 5 votes |
export function NewNickname(props: { show: boolean; hide: () => void }) {
const [value, set_value] = React.useState<null | string>(null);
const [key_value, set_key_value] = React.useState<null | string>(null);
return (
<Dialog onClose={props.hide} open={props.show}>
<DialogTitle>Create a new User</DialogTitle>
<DialogContent>
<DialogContentText>
The key and nickname for the new person. Keys must be
unique.
</DialogContentText>
<TextField
onChange={(ev) => set_value(ev.currentTarget.value)}
value={value}
autoFocus
margin="dense"
label="Name"
name="name"
type="text"
fullWidth
variant="standard"
/>
<TextField
onChange={(ev) => set_key_value(ev.currentTarget.value)}
value={key_value}
autoFocus
margin="dense"
label="Key Hash"
name="keyhash"
type="text"
fullWidth
variant="standard"
/>
</DialogContent>
<DialogActions>
<Button onClick={props.hide}>Cancel</Button>
<Button
color="success"
onClick={async (ev) => {
if (value !== null && key_value) {
await window.electron.chat.add_user(
value,
key_value
);
}
props.hide();
}}
>
Create
</Button>
</DialogActions>
</Dialog>
);
}
Example #11
Source File: DeleteBucket.tsx From console with GNU Affero General Public License v3.0 | 5 votes |
DeleteBucket = ({
closeDeleteModalAndRefresh,
deleteOpen,
selectedBucket,
}: IDeleteBucketProps) => {
const dispatch = useDispatch();
const onDelSuccess = () => closeDeleteModalAndRefresh(true);
const onDelError = (err: ErrorResponseHandler) =>
dispatch(setErrorSnackMessage(err));
const onClose = () => closeDeleteModalAndRefresh(false);
const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError);
if (!selectedBucket) {
return null;
}
const onConfirmDelete = () => {
invokeDeleteApi("DELETE", `/api/v1/buckets/${selectedBucket}`, {
name: selectedBucket,
});
};
return (
<ConfirmDialog
title={`Delete Bucket`}
confirmText={"Delete"}
isOpen={deleteOpen}
titleIcon={<ConfirmDeleteIcon />}
isLoading={deleteLoading}
onConfirm={onConfirmDelete}
onClose={onClose}
confirmationContent={
<DialogContentText>
Are you sure you want to delete bucket <b>{selectedBucket}</b>? <br />
A bucket can only be deleted if it's empty.
</DialogContentText>
}
/>
);
}
Example #12
Source File: FeedbackDialog.tsx From airmessage-web with Apache License 2.0 | 5 votes |
/**
* A dialog that presents help and feedback options
*/
export default function FeedbackDialog(props: {isOpen: boolean, onDismiss: () => void}) {
const propsOnDismiss = props.onDismiss;
const onClickEmail = useCallback(async () => {
const body =
`\n\n---------- DEVICE INFORMATION ----------` +
Object.entries(await getPlatformUtils().getExtraEmailDetails())
.map(([key, value]) => `\n${key}: ${value}`)
.join("") +
`\nUser agent: ${navigator.userAgent}` +
`\nClient version: ${appVersion}` +
`\nCommunications version: ${getActiveCommVer()?.join(".")} (target ${targetCommVerString})` +
`\nProxy type: ${getActiveProxyType()}` +
`\nServer system version: ${getServerSystemVersion()}` +
`\nServer software version: ${getServerSoftwareVersion()}`;
const url = `mailto:${supportEmail}?subject=${encodeURIComponent("AirMessage feedback")}&body=${encodeURIComponent(body)}`;
window.open(url, "_blank");
propsOnDismiss();
}, [propsOnDismiss]);
const onClickCommunity = useCallback(() => {
window.open(communityPage, "_blank");
propsOnDismiss();
}, [propsOnDismiss]);
return (
<Dialog
open={props.isOpen}
onClose={props.onDismiss}>
<DialogTitle>Help and feedback</DialogTitle>
<DialogContent>
<DialogContentText>
Have a bug to report, a feature to suggest, or anything else to say? Contact us or discuss with others using the links below.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={onClickEmail} color="primary">
Send E-Mail
</Button>
<Button onClick={onClickCommunity} color="primary" autoFocus>
Open community subreddit
</Button>
</DialogActions>
</Dialog>
);
}
Example #13
Source File: DeleteServiceAccount.tsx From console with GNU Affero General Public License v3.0 | 5 votes |
DeleteServiceAccount = ({
classes,
closeDeleteModalAndRefresh,
deleteOpen,
selectedServiceAccount,
}: IDeleteServiceAccountProps) => {
const dispatch = useDispatch();
const onDelSuccess = () => closeDeleteModalAndRefresh(true);
const onDelError = (err: ErrorResponseHandler) =>
dispatch(setErrorSnackMessage(err));
const onClose = () => closeDeleteModalAndRefresh(false);
const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError);
if (!selectedServiceAccount) {
return null;
}
const onConfirmDelete = () => {
invokeDeleteApi(
"DELETE",
`/api/v1/service-accounts/${encodeURLString(selectedServiceAccount)}`
);
};
return (
<ConfirmDialog
title={`Delete Service Account`}
confirmText={"Delete"}
isOpen={deleteOpen}
titleIcon={<ConfirmDeleteIcon />}
isLoading={deleteLoading}
onConfirm={onConfirmDelete}
onClose={onClose}
confirmationContent={
<DialogContentText>
Are you sure you want to delete service account{" "}
<b className={classes.wrapText}>{selectedServiceAccount}</b>?
</DialogContentText>
}
/>
);
}
Example #14
Source File: PurchaseDialogueBody.tsx From mojito_pdm with Creative Commons Attribution Share Alike 4.0 International | 5 votes |
PurchaseDialogueBody: React.FC<IPurchaseDialogueBody> = ({spawncode, price, setDialogueOpen, setModalOpen}) => {
const [colour, setColour] = useState<RgbColor>({r: 0, g: 0, b: 0})
const handleClose = () => {
setDialogueOpen(false)
}
const {setVisible} = useVisibility()
const coloursEnabled = useRecoilValue(GlobalState.customColours)
const handleAccept = async () => {
setDialogueOpen(false)
setModalOpen(false)
try {
await fetchNui("buy_vehicle", {
vehicle: spawncode,
colour: coloursEnabled ? colour : null
})
await fetchNui("exit")
setVisible(false)
} catch (e) {
console.error(e)
}
}
return (
<>
<DialogTitle>Are you sure?</DialogTitle>
<DialogContent>
<DialogContentText>
Do you want to purchase this vehicle for {price}?
</DialogContentText>
{coloursEnabled &&
<DialogContentText>
<br />
Pick a colour, any colour:
<RgbColorPicker color={colour} onChange={setColour} />
</DialogContentText>
}
</DialogContent>
<DialogActions>
<Button color="success" variant="outlined" onClick={handleAccept}>Yes</Button>
<Button color="error" variant="outlined" onClick={handleClose}>Cancel</Button>
</DialogActions>
</>
)
}
Example #15
Source File: EnableVersioningModal.tsx From console with GNU Affero General Public License v3.0 | 5 votes |
EnableVersioningModal = ({
closeVersioningModalAndRefresh,
modalOpen,
selectedBucket,
versioningCurrentState,
}: IVersioningEventProps) => {
const dispatch = useDispatch();
const [versioningLoading, setVersioningLoading] = useState<boolean>(false);
const enableVersioning = () => {
if (versioningLoading) {
return;
}
setVersioningLoading(true);
api
.invoke("PUT", `/api/v1/buckets/${selectedBucket}/versioning`, {
versioning: !versioningCurrentState,
})
.then(() => {
setVersioningLoading(false);
closeVersioningModalAndRefresh(true);
})
.catch((err: ErrorResponseHandler) => {
setVersioningLoading(false);
dispatch(setErrorSnackMessage(err));
});
};
return (
<ConfirmDialog
title={`Versioning on Bucket`}
confirmText={versioningCurrentState ? "Disable" : "Enable"}
isOpen={modalOpen}
isLoading={versioningLoading}
titleIcon={<ConfirmModalIcon />}
onConfirm={enableVersioning}
confirmButtonProps={{
color: "primary",
variant: "contained",
}}
onClose={() => {
closeVersioningModalAndRefresh(false);
}}
confirmationContent={
<DialogContentText id="alert-dialog-description">
Are you sure you want to{" "}
<strong>{versioningCurrentState ? "disable" : "enable"}</strong>{" "}
versioning for this bucket?
{versioningCurrentState && (
<Fragment>
<br />
<br />
<strong>File versions won't be automatically deleted.</strong>
</Fragment>
)}
</DialogContentText>
}
/>
);
}
Example #16
Source File: Settings.tsx From sapio-studio with Mozilla Public License 2.0 | 4 votes |
export function SettingsInner() {
const [idx, set_idx] = React.useState<number>(0);
const [dialog_node, set_dialog_node] = React.useState<
[string | null, string[]]
>([null, []]);
const handleChange = (_: any, idx: number) => {
set_idx(idx);
};
const test_bitcoind = async () => {
window.electron
.bitcoin_command([{ method: 'getbestblockhash', parameters: [] }])
.then((h) =>
set_dialog_node(['Connection Seems OK:', [`Best Hash ${h[0]}`]])
)
.catch((e) => {
console.log('GOT', JSON.stringify(e));
const r = e.message;
if (typeof e.message === 'string') {
const err = JSON.parse(r);
if (
err instanceof Object &&
'code' in err &&
'name' in err &&
'message' in err
) {
set_dialog_node([
'¡Connection Not Working!',
[
`Name: ${err.name}`,
`Message: ${err.message}`,
`Error Code: ${err.code}`,
],
]);
return;
} else if (typeof err === 'string') {
set_dialog_node([
'¡Connection Not Working!',
[`${err}`],
]);
return;
}
}
set_dialog_node(['¡Unknown Error!', [`${r.toString()}`]]);
});
};
const test_sapio = async () => {
window.electron.sapio
.show_config()
.then((conf) => {
if ('ok' in conf)
set_dialog_node([
'Sapio-CLI is Working!\nUsing Configuration:',
[`${conf.ok}`],
]);
else
set_dialog_node(['¡Configuration Error!', [`${conf.err}`]]);
})
.catch((e) =>
set_dialog_node(['¡Configuration Error!', [`${e.toString()}`]])
);
};
const check_emulator = async () => {
window.electron.emulator.read_log().then((log) => {
if (log.length) {
const json = JSON.parse(log);
set_dialog_node([
'Emulator Status:',
[
`interface: ${json.interface}`,
`pk: ${json.pk}`,
`sync: ${json.sync}`,
],
]);
} else {
set_dialog_node(['Emulator Status:', ['Not Running']]);
}
});
};
return (
<div className="Settings">
<Box className="SettingsNav">
<Tabs
orientation="vertical"
value={idx}
onChange={handleChange}
aria-label="basic tabs example"
>
<Tab label="Guide"></Tab>
<Tab label="Sapio CLI"></Tab>
<Tab label="Bitcoin"></Tab>
<Tab label="Emulator"></Tab>
<Tab label="Display"></Tab>
</Tabs>
</Box>
<Box className="SettingsPanes">
<Dialog
open={
Boolean(dialog_node[0]) ||
Boolean(dialog_node[1].length)
}
onClose={() => set_dialog_node([null, []])}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{dialog_node[0]}
</DialogTitle>
<DialogContent>
<div id="alert-dialog-description">
{dialog_node[1].map((txt) => (
<DialogContentText key={txt}>
{txt}
</DialogContentText>
))}
</div>
</DialogContent>
<DialogActions>
<Button onClick={() => set_dialog_node([null, []])}>
Close
</Button>
</DialogActions>
</Dialog>
<Guide idx={idx} my_idx={0} />
<SettingPane name={'sapio_cli'} value={idx} idx={1}>
<Button
onClick={test_sapio}
variant="contained"
color="info"
size="large"
>
Test Sapio-Cli
</Button>
</SettingPane>
<SettingPane name={'bitcoin'} value={idx} idx={2}>
<Button
onClick={test_bitcoind}
variant="contained"
color="info"
size="large"
>
Test Connection
</Button>
</SettingPane>
<SettingPane name={'local_oracle'} value={idx} idx={3}>
<Button
variant="contained"
color="success"
size="large"
onClick={window.electron.emulator.start}
>
Start
</Button>
<Button
sx={{ marginLeft: '20px' }}
variant="contained"
color="error"
size="large"
onClick={window.electron.emulator.kill}
>
Kill
</Button>
<Button
sx={{ marginLeft: '20px' }}
variant="contained"
color="info"
size="large"
onClick={check_emulator}
>
Check Status
</Button>
</SettingPane>
<SettingPane name={'display'} value={idx} idx={4} />
</Box>
</div>
);
}
Example #17
Source File: GroupSettings.tsx From abrechnung with GNU Affero General Public License v3.0 | 4 votes |
export default function GroupSettings({ group }) {
const [showLeaveModal, setShowLeaveModal] = useState(false);
const history = useHistory();
const userPermissions = useRecoilValue(currUserPermissions(group.id));
const [isEditing, setIsEditing] = useState(false);
useTitle(`${group.name} - Settings`);
const startEdit = () => {
setIsEditing(true);
};
const stopEdit = () => {
setIsEditing(false);
};
const handleSubmit = (values, { setSubmitting }) => {
updateGroupMetadata({
groupID: group.id,
name: values.name,
description: values.description,
currencySymbol: values.currencySymbol,
terms: values.terms,
addUserAccountOnJoin: values.addUserAccountOnJoin,
})
.then((res) => {
setSubmitting(false);
setIsEditing(false);
})
.catch((err) => {
setSubmitting(false);
toast.error(err);
});
};
const confirmLeaveGroup = () => {
leaveGroup({ groupID: group.id })
.then((res) => {
history.push("/");
})
.catch((err) => {
toast.error(err);
});
};
return (
<MobilePaper>
{userPermissions.is_owner ? (
<Alert severity="info">You are an owner of this group</Alert>
) : !userPermissions.can_write ? (
<Alert severity="info">You only have read access to this group</Alert>
) : null}
<Formik
initialValues={{
name: group.name,
description: group.description,
terms: group.terms,
currencySymbol: group.currency_symbol,
addUserAccountOnJoin: group.add_user_account_on_join,
}}
onSubmit={handleSubmit}
validationSchema={validationSchema}
enableReinitialize={true}
>
{({ values, handleBlur, handleChange, handleSubmit, isSubmitting }) => (
<Form onSubmit={handleSubmit}>
<DisabledTextField
variant="standard"
margin="normal"
required
fullWidth
type="text"
label="Name"
name="name"
disabled={!userPermissions.can_write || !isEditing}
onBlur={handleBlur}
onChange={handleChange}
value={values.name}
/>
<DisabledTextField
variant="standard"
margin="normal"
fullWidth
type="text"
name="description"
label="Description"
disabled={!userPermissions.can_write || !isEditing}
onBlur={handleBlur}
onChange={handleChange}
value={values.description}
/>
<DisabledTextField
variant="standard"
margin="normal"
required
fullWidth
type="text"
name="currencySymbol"
label="Currency"
disabled={!userPermissions.can_write || !isEditing}
onBlur={handleBlur}
onChange={handleChange}
value={values.currencySymbol}
/>
<DisabledTextField
variant="standard"
multiline={true}
margin="normal"
fullWidth
type="text"
name="terms"
label="Terms"
disabled={!userPermissions.can_write || !isEditing}
onBlur={handleBlur}
onChange={handleChange}
value={values.terms}
/>
<FormGroup>
<DisabledFormControlLabel
control={
<Checkbox
name="addUserAccountOnJoin"
disabled={!userPermissions.can_write || !isEditing}
onBlur={handleBlur}
onChange={handleChange}
checked={values.addUserAccountOnJoin}
/>
}
label="Automatically add accounts for newly joined group members"
/>
</FormGroup>
{isSubmitting && <LinearProgress />}
<Grid container justifyContent="space-between" style={{ marginTop: 10 }}>
<div>
{userPermissions.can_write && isEditing && (
<>
<Button
type="submit"
variant="contained"
color="primary"
disabled={isSubmitting}
startIcon={<Save />}
>
Save
</Button>
<Button
variant="contained"
color="error"
disabled={isSubmitting}
onClick={stopEdit}
startIcon={<Cancel />}
sx={{ ml: 1 }}
>
Cancel
</Button>
</>
)}
{userPermissions.can_write && !isEditing && (
<Button
variant="contained"
color="primary"
disabled={isSubmitting}
onClick={startEdit}
startIcon={<Edit />}
>
Edit
</Button>
)}
</div>
<Button variant="contained" onClick={() => setShowLeaveModal(true)}>
Leave Group
</Button>
</Grid>
</Form>
)}
</Formik>
{/*<List>*/}
{/* <ListItem>*/}
{/* <ListItemText primary="Created" secondary={group.created}/>*/}
{/* </ListItem>*/}
{/* <ListItem>*/}
{/* <ListItemText primary="Joined" secondary={group.joined}/>*/}
{/* </ListItem>*/}
{/*</List>*/}
<Dialog open={showLeaveModal} onClose={() => setShowLeaveModal(false)}>
<DialogTitle>Leave Group</DialogTitle>
<DialogContent>
<DialogContentText>
<span>
Are you sure you want to leave the group {group.name}. If you are the last member to leave
this group it will be deleted and its transaction will be lost forever...
</span>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button color="secondary" onClick={confirmLeaveGroup}>
Yes pls
</Button>
<Button color="primary" onClick={() => setShowLeaveModal(false)}>
No
</Button>
</DialogActions>
</Dialog>
</MobilePaper>
);
}
Example #18
Source File: index.tsx From ExpressLRS-Configurator with GNU General Public License v3.0 | 4 votes |
ConfiguratorView: FunctionComponent<ConfiguratorViewProps> = (props) => {
const {
gitRepository,
selectedDevice,
networkDevices,
onDeviceChange,
deviceType,
} = props;
const [viewState, setViewState] = useState<ViewState>(
ViewState.Configuration
);
const { setAppStatus } = useAppState();
const [progressNotifications, setProgressNotifications] = useState<
BuildProgressNotification[]
>([]);
const progressNotificationsRef = useRef<BuildProgressNotification[]>([]);
const [
lastProgressNotification,
setLastProgressNotification,
] = useState<BuildProgressNotification | null>(null);
useBuildProgressNotificationsSubscription({
onSubscriptionData: (options) => {
const args = options.subscriptionData.data?.buildProgressNotifications;
if (args !== undefined) {
const newNotificationsList = [
...progressNotificationsRef.current,
args,
];
progressNotificationsRef.current = newNotificationsList;
setProgressNotifications(newNotificationsList);
setLastProgressNotification(args);
}
},
});
/*
We batch log events in order to save React.js state updates and rendering performance.
*/
const [logs, setLogs] = useState<string>('');
const logsRef = useRef<string[]>([]);
const eventsBatcherRef = useRef<EventsBatcher<string> | null>(null);
useEffect(() => {
eventsBatcherRef.current = new EventsBatcher<string>(200);
eventsBatcherRef.current.onBatch((newLogs) => {
const newLogsList = [...logsRef.current, ...newLogs];
logsRef.current = newLogsList;
setLogs(newLogsList.join(''));
});
}, []);
useBuildLogUpdatesSubscription({
fetchPolicy: 'network-only',
onSubscriptionData: (options) => {
const args = options.subscriptionData.data?.buildLogUpdates.data;
if (args !== undefined && eventsBatcherRef.current !== null) {
eventsBatcherRef.current.enqueue(args);
}
},
});
const [
firmwareVersionData,
setFirmwareVersionData,
] = useState<FirmwareVersionDataInput | null>(null);
const [firmwareVersionErrors, setFirmwareVersionErrors] = useState<Error[]>(
[]
);
const onFirmwareVersionData = useCallback(
(data: FirmwareVersionDataInput) => {
setFirmwareVersionErrors([]);
setFirmwareVersionData(data);
},
[]
);
const [deviceTarget, setDeviceTarget] = useState<Target | null>(null);
const [deviceTargetErrors, setDeviceTargetErrors] = useState<Error[]>([]);
const onDeviceTarget = useCallback(
(data: Target | null) => {
setDeviceTargetErrors([]);
setDeviceTarget(data);
// if target was manually changed, set selected device to null
onDeviceChange(null);
},
[onDeviceChange]
);
const [deviceTargets, setDeviceTargets] = useState<Device[] | null>(null);
const [
fetchDeviceTargets,
{
loading: loadingTargets,
data: targetsResponse,
error: targetsResponseError,
},
] = useAvailableFirmwareTargetsLazyQuery({
fetchPolicy: 'network-only',
});
const [
fetchLuaScript,
{ data: luaScriptResponse, error: luaScriptResponseError },
] = useLuaScriptLazyQuery();
const device = useMemo(() => {
return deviceTargets?.find((d) => {
return d.targets.find((target) => target.id === deviceTarget?.id);
});
}, [deviceTarget, deviceTargets]);
useEffect(() => {
if (
firmwareVersionData === null ||
validateFirmwareVersionData(firmwareVersionData).length > 0
) {
setDeviceTargets(null);
} else {
fetchDeviceTargets({
variables: {
source: firmwareVersionData.source as FirmwareSource,
gitBranch: firmwareVersionData.gitBranch!,
gitTag: firmwareVersionData.gitTag!,
gitCommit: firmwareVersionData.gitCommit!,
localPath: firmwareVersionData.localPath!,
gitPullRequest: firmwareVersionData.gitPullRequest,
gitRepository: {
url: gitRepository.url,
owner: gitRepository.owner,
repositoryName: gitRepository.repositoryName,
rawRepoUrl: gitRepository.rawRepoUrl,
srcFolder: gitRepository.srcFolder,
},
},
});
}
}, [gitRepository, firmwareVersionData, fetchDeviceTargets]);
useEffect(() => {
if (targetsResponse?.availableFirmwareTargets) {
setDeviceTargets([...targetsResponse.availableFirmwareTargets]);
} else {
setDeviceTargets(null);
}
}, [targetsResponse]);
const [
deviceOptionsFormData,
setDeviceOptionsFormData,
] = useState<DeviceOptionsFormData>({
userDefinesTxt: '',
userDefinesMode: UserDefinesMode.UserInterface,
userDefineOptions: [],
});
const handleDeviceOptionsResponse = async (
deviceOptionsResponse: TargetDeviceOptionsQuery
) => {
const storage = new ApplicationStorage();
const deviceName = device?.name || null;
const userDefineOptions = await mergeWithDeviceOptionsFromStorage(
storage,
deviceName,
{
...deviceOptionsFormData,
userDefineOptions: [...deviceOptionsResponse.targetDeviceOptions],
}
);
// if a network device is selected, merge in its options
if (selectedDevice && networkDevices.has(selectedDevice)) {
const networkDevice = networkDevices.get(selectedDevice);
userDefineOptions.userDefineOptions = userDefineOptions.userDefineOptions.map(
(userDefineOption) => {
const networkDeviceOption = networkDevice?.options.find(
(item) => item.key === userDefineOption.key
);
const newUserDefineOption = { ...userDefineOption };
if (networkDeviceOption) {
newUserDefineOption.enabled = networkDeviceOption.enabled;
newUserDefineOption.value = networkDeviceOption.value;
}
return newUserDefineOption;
}
);
}
setDeviceOptionsFormData(userDefineOptions);
};
const [
fetchOptions,
{
loading: loadingOptions,
data: deviceOptionsResponse,
error: deviceOptionsResponseError,
},
] = useTargetDeviceOptionsLazyQuery({
fetchPolicy: 'network-only',
onCompleted: (data) => {
handleDeviceOptionsResponse(data).catch((err) => {
console.error('failed to handle device options response', err);
});
},
});
useEffect(() => {
if (
deviceTarget === null ||
firmwareVersionData === null ||
validateFirmwareVersionData(firmwareVersionData).length > 0
) {
setDeviceOptionsFormData({
userDefinesTxt: '',
userDefinesMode: UserDefinesMode.UserInterface,
userDefineOptions: [],
});
} else {
fetchOptions({
variables: {
target: deviceTarget.name,
source: firmwareVersionData.source as FirmwareSource,
gitBranch: firmwareVersionData.gitBranch!,
gitTag: firmwareVersionData.gitTag!,
gitCommit: firmwareVersionData.gitCommit!,
localPath: firmwareVersionData.localPath!,
gitPullRequest: firmwareVersionData.gitPullRequest,
gitRepository: {
url: gitRepository.url,
owner: gitRepository.owner,
repositoryName: gitRepository.repositoryName,
rawRepoUrl: gitRepository.rawRepoUrl,
srcFolder: gitRepository.srcFolder,
},
},
});
}
}, [deviceTarget, firmwareVersionData, gitRepository, fetchOptions]);
const onResetToDefaults = () => {
const handleReset = async () => {
if (deviceOptionsResponse === undefined || deviceTarget === null) {
// eslint-disable-next-line no-alert
alert(`deviceOptionsResponse is undefined`);
return;
}
const deviceName = device?.name || null;
if (deviceName) {
const storage = new ApplicationStorage();
await storage.removeDeviceOptions(deviceName);
const userDefineOptions = await mergeWithDeviceOptionsFromStorage(
storage,
deviceName,
{
...deviceOptionsFormData,
userDefineOptions: [...deviceOptionsResponse.targetDeviceOptions],
}
);
setDeviceOptionsFormData(userDefineOptions);
}
};
handleReset().catch((err) => {
console.error(`failed to reset device options form data: ${err}`);
});
};
const onUserDefines = useCallback(
(data: DeviceOptionsFormData) => {
setDeviceOptionsFormData(data);
if (deviceTarget !== null) {
const storage = new ApplicationStorage();
const deviceName = device?.name;
if (deviceName) {
persistDeviceOptions(storage, deviceName, data).catch((err) => {
console.error(`failed to persist user defines: ${err}`);
});
}
}
},
[deviceTarget, deviceTargets]
);
const [
buildFlashFirmwareMutation,
{
loading: buildInProgress,
data: response,
error: buildFlashErrorResponse,
},
] = useBuildFlashFirmwareMutation();
useEffect(() => {
const arg = response?.buildFlashFirmware?.firmwareBinPath;
if (arg !== undefined && arg !== null && arg?.length > 0) {
const body: OpenFileLocationRequestBody = {
path: arg,
};
ipcRenderer.send(IpcRequest.OpenFileLocation, body);
}
}, [response]);
const isTX = useMemo(() => {
if (deviceTarget) {
return deviceTarget.name?.indexOf('_TX_') > -1;
}
return false;
}, [deviceTarget]);
const hasLuaScript = useMemo(() => {
return deviceType === DeviceType.ExpressLRS && isTX;
}, [deviceType, isTX]);
useEffect(() => {
if (firmwareVersionData && isTX && hasLuaScript) {
fetchLuaScript({
variables: {
source: firmwareVersionData.source as FirmwareSource,
gitBranch: firmwareVersionData.gitBranch!,
gitTag: firmwareVersionData.gitTag!,
gitCommit: firmwareVersionData.gitCommit!,
localPath: firmwareVersionData.localPath!,
gitPullRequest: firmwareVersionData.gitPullRequest,
gitRepository: {
url: gitRepository.url,
owner: gitRepository.owner,
repositoryName: gitRepository.repositoryName,
rawRepoUrl: gitRepository.rawRepoUrl,
srcFolder: gitRepository.srcFolder,
},
},
});
}
}, [gitRepository, firmwareVersionData, fetchLuaScript, isTX, hasLuaScript]);
/*
Display Electron.js confirmation dialog if user wants to shutdown the app
when build is in progress.
*/
useEffect(() => {
const body: UpdateBuildStatusRequestBody = {
buildInProgress,
};
ipcRenderer.send(IpcRequest.UpdateBuildStatus, body);
}, [buildInProgress]);
const [serialDevice, setSerialDevice] = useState<string | null>(null);
const onSerialDevice = (newSerialDevice: string | null) => {
setSerialDevice(newSerialDevice);
};
const [wifiDevice, setWifiDevice] = useState<string | null>(null);
const onWifiDevice = useCallback((newWifiDevice: string | null) => {
setWifiDevice(newWifiDevice);
}, []);
const [serialPortRequired, setSerialPortRequired] = useState<boolean>(false);
const [wifiDeviceRequired, setWifiDeviceRequired] = useState<boolean>(false);
useEffect(() => {
if (
deviceTarget &&
(deviceTarget.flashingMethod === FlashingMethod.BetaflightPassthrough ||
deviceTarget.flashingMethod === FlashingMethod.UART)
) {
setSerialPortRequired(true);
} else {
setSerialPortRequired(false);
}
if (deviceTarget && deviceTarget.flashingMethod === FlashingMethod.WIFI) {
setWifiDeviceRequired(true);
} else {
setWifiDeviceRequired(false);
}
}, [deviceTarget, deviceTarget, deviceTargets]);
const [
deviceOptionsValidationErrors,
setDeviceOptionsValidationErrors,
] = useState<Error[] | null>(null);
const reset = () => {
logsRef.current = [];
progressNotificationsRef.current = [];
setLogs('');
setFirmwareVersionErrors([]);
setDeviceTargetErrors([]);
setDeviceOptionsValidationErrors([]);
setProgressNotifications([]);
setLastProgressNotification(null);
};
const onBack = () => {
reset();
setViewState(ViewState.Configuration);
setAppStatus(AppStatus.Interactive);
};
const getAbbreviatedDeviceName = (item: Device) => {
return item.abbreviatedName?.slice(0, 16) ?? item.name?.slice(0, 16);
};
const [currentJobType, setCurrentJobType] = useState<BuildJobType>(
BuildJobType.Build
);
const sendJob = (type: BuildJobType) => {
reset();
setCurrentJobType(type);
// Validate firmware source
if (firmwareVersionData === null) {
setFirmwareVersionErrors([new Error('Please select firmware source')]);
return;
}
const sourceErrors = validateFirmwareVersionData(firmwareVersionData);
if (sourceErrors.length > 0) {
setFirmwareVersionErrors(sourceErrors);
return;
}
// Validate device target
if (deviceTarget === null) {
setDeviceTargetErrors([new Error('Please select a device target')]);
return;
}
// Validate device options
if (deviceOptionsFormData === null) {
setDeviceTargetErrors([
new Error('Please configure your device options'),
]);
return;
}
switch (deviceOptionsFormData.userDefinesMode) {
case UserDefinesMode.Manual:
break;
case UserDefinesMode.UserInterface:
const errs = new UserDefinesValidator().validate(
deviceOptionsFormData.userDefineOptions
);
if (errs.length > 0) {
setDeviceOptionsValidationErrors(errs);
return;
}
break;
default:
break;
}
let uploadPort: string | undefined;
if (serialPortRequired && serialDevice != null) {
uploadPort = serialDevice;
} else if (wifiDeviceRequired && wifiDevice !== null) {
uploadPort = wifiDevice;
}
const userDefines = deviceOptionsFormData.userDefineOptions.map((item) => ({
key: item.key,
value: item.value,
enabled: item.enabled,
enumValues: item.enumValues,
type: item.type,
}));
if (device?.parent && device?.name) {
const deviceName = getAbbreviatedDeviceName(device);
// add the user define for the device name
userDefines.push({
key: UserDefineKey.DEVICE_NAME,
value: deviceName,
enabled: true,
enumValues: null,
type: UserDefineKind.Text,
});
}
const input: BuildFlashFirmwareInput = {
type,
firmware: firmwareVersionData,
target: deviceTarget.name,
userDefinesTxt: deviceOptionsFormData.userDefinesTxt,
userDefinesMode: deviceOptionsFormData.userDefinesMode,
userDefines,
serialDevice: uploadPort,
};
buildFlashFirmwareMutation({
variables: {
input,
gitRepository: {
url: gitRepository.url,
owner: gitRepository.owner,
repositoryName: gitRepository.repositoryName,
rawRepoUrl: gitRepository.rawRepoUrl,
srcFolder: gitRepository.srcFolder,
},
},
});
setViewState(ViewState.Compiling);
setAppStatus(AppStatus.Busy);
};
useEffect(() => {
if (
!buildInProgress &&
response?.buildFlashFirmware?.success !== undefined
) {
window.scrollTo(0, document.body.scrollHeight);
}
}, [buildInProgress, response]);
const onBuild = () => sendJob(BuildJobType.Build);
const onBuildAndFlash = () => sendJob(BuildJobType.BuildAndFlash);
const onForceFlash = () => sendJob(BuildJobType.ForceFlash);
const deviceTargetRef = useRef<HTMLDivElement | null>(null);
const deviceOptionsRef = useRef<HTMLDivElement | null>(null);
const [
deviceSelectErrorDialogOpen,
setDeviceSelectErrorDialogOpen,
] = useState<boolean>(false);
const handleSelectedDeviceChange = useCallback(
(deviceName: string) => {
const dnsDevice = networkDevices.get(deviceName);
if (dnsDevice) {
const dnsDeviceName = dnsDevice.deviceName?.toUpperCase();
const dnsDeviceTarget = dnsDevice.target.toUpperCase();
let deviceMatches: Device[] | undefined = [];
// try to find the device by the deviceName
deviceMatches = deviceTargets?.filter((item) => {
return getAbbreviatedDeviceName(item).toUpperCase() === dnsDeviceName;
});
// if no matches found by deviceName, then use the target
if (
deviceMatches?.length === 0 &&
dnsDeviceTarget.trim().length !== 0
) {
deviceMatches = deviceTargets?.filter((item) => {
// only match on a device that doesn't have a parent, which means it
// is not an alias of another device
return (
!item.parent &&
item.targets.find((target) => {
const baseTargetName = target.name.split('_via_')[0];
return baseTargetName.toUpperCase() === dnsDeviceTarget;
})
);
});
}
// if no device is found that matches the target
if (!deviceMatches || deviceMatches.length === 0) {
console.error(
`no device matches found for target ${dnsDeviceTarget}!`
);
setDeviceSelectErrorDialogOpen(true);
return;
}
// if multiple device matches are found, then don't select any of them
// we do not know which one is correct and do not want to pick the wrong device.
if (deviceMatches.length > 1) {
console.error(
`multiple device matches found for target ${dnsDeviceTarget}!`
);
setDeviceSelectErrorDialogOpen(true);
return;
}
const deviceMatch = deviceMatches[0];
const dTarget =
deviceMatch?.targets.find((target) => {
return target.flashingMethod === FlashingMethod.WIFI;
}) ||
deviceMatch?.targets[0] ||
null;
if (dTarget !== deviceTarget) {
setDeviceTarget(dTarget);
deviceTargetRef?.current?.scrollIntoView({ behavior: 'smooth' });
}
setWifiDevice(dnsDevice.ip);
}
},
[deviceTarget, deviceTargets, networkDevices]
);
useEffect(() => {
if (selectedDevice) {
handleSelectedDeviceChange(selectedDevice);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedDevice]);
const luaDownloadButton = () => {
if (
hasLuaScript &&
luaScriptResponse &&
luaScriptResponse.luaScript.fileLocation &&
luaScriptResponse.luaScript.fileLocation.length > 0
) {
return (
<Button
sx={styles.button}
color="primary"
size="large"
variant="contained"
href={luaScriptResponse?.luaScript.fileLocation ?? ''}
download
>
Download LUA script
</Button>
);
}
return null;
};
const handleDeviceSelectErrorDialogClose = useCallback(() => {
setDeviceSelectErrorDialogOpen(false);
}, []);
const saveBuildLogToFile = useCallback(async () => {
const saveFileRequestBody: SaveFileRequestBody = {
data: logs,
defaultPath: `ExpressLRSBuildLog_${new Date()
.toISOString()
.replace(/[^0-9]/gi, '')}.txt`,
};
const result: SaveFileResponseBody = await ipcRenderer.invoke(
IpcRequest.SaveFile,
saveFileRequestBody
);
if (result.success) {
const openFileLocationRequestBody: OpenFileLocationRequestBody = {
path: result.path,
};
ipcRenderer.send(
IpcRequest.OpenFileLocation,
openFileLocationRequestBody
);
}
}, [logs]);
return (
<MainLayout>
{viewState === ViewState.Configuration && (
<>
<Card>
<CardTitle icon={<SettingsIcon />} title="Firmware version" />
<Divider />
<CardContent>
<FirmwareVersionForm
onChange={onFirmwareVersionData}
data={firmwareVersionData}
gitRepository={gitRepository}
/>
<ShowAlerts severity="error" messages={firmwareVersionErrors} />
</CardContent>
<Divider />
<CardTitle icon={<SettingsIcon />} title="Target" />
<Divider />
<CardContent ref={deviceTargetRef}>
{firmwareVersionData === null ||
(validateFirmwareVersionData(firmwareVersionData).length >
0 && (
<Alert severity="info">
<AlertTitle>Notice</AlertTitle>
Please select a firmware version first
</Alert>
))}
{!loadingTargets && !targetsResponseError && (
<DeviceTargetForm
currentTarget={deviceTarget}
onChange={onDeviceTarget}
firmwareVersionData={firmwareVersionData}
deviceOptions={deviceTargets}
/>
)}
<Loader loading={loadingTargets} />
{luaDownloadButton()}
{hasLuaScript && (
<ShowAlerts
severity="error"
messages={luaScriptResponseError}
/>
)}
<ShowAlerts severity="error" messages={targetsResponseError} />
<ShowAlerts severity="error" messages={deviceTargetErrors} />
</CardContent>
<Divider />
<CardTitle
icon={<SettingsIcon />}
title={
<div ref={deviceOptionsRef}>
Device options{' '}
{deviceOptionsFormData.userDefinesMode ===
UserDefinesMode.UserInterface &&
deviceTarget !== null &&
!loadingOptions && (
<Tooltip
placement="top"
arrow
title={
<div>
Reset device options to the recommended defaults on
this device target. Except for your custom binding
phrase.
</div>
}
>
<Button onClick={onResetToDefaults} size="small">
Reset
</Button>
</Tooltip>
)}
</div>
}
/>
<Divider />
<CardContent>
{!loadingOptions && (
<DeviceOptionsForm
target={deviceTarget?.name ?? null}
deviceOptions={deviceOptionsFormData}
firmwareVersionData={firmwareVersionData}
onChange={onUserDefines}
/>
)}
{deviceOptionsFormData.userDefinesMode ===
UserDefinesMode.UserInterface &&
(firmwareVersionData === null ||
validateFirmwareVersionData(firmwareVersionData).length > 0 ||
deviceTarget === null) && (
<Alert severity="info">
<AlertTitle>Notice</AlertTitle>
Please select a firmware version and device target first
</Alert>
)}
<ShowAlerts
severity="error"
messages={deviceOptionsResponseError}
/>
<ShowAlerts
severity="error"
messages={deviceOptionsValidationErrors}
/>
<Loader loading={loadingOptions} />
</CardContent>
<Divider />
<CardTitle icon={<SettingsIcon />} title="Actions" />
<Divider />
<CardContent>
<UserDefinesAdvisor
deviceOptionsFormData={deviceOptionsFormData}
/>
<div>
{serialPortRequired && (
<SerialDeviceSelect
serialDevice={serialDevice}
onChange={onSerialDevice}
/>
)}
{wifiDeviceRequired && (
<WifiDeviceSelect
wifiDevice={wifiDevice}
wifiDevices={Array.from(networkDevices.values()).filter(
(item) => {
return deviceTarget?.name
?.toUpperCase()
.startsWith(item.target.toUpperCase());
}
)}
onChange={onWifiDevice}
/>
)}
<Button
sx={styles.button}
size="large"
variant="contained"
onClick={onBuild}
>
Build
</Button>
{deviceTarget?.flashingMethod !== FlashingMethod.Radio && (
<SplitButton
sx={styles.button}
size="large"
variant="contained"
options={[
{
label: 'Build & Flash',
value: BuildJobType.BuildAndFlash,
},
{
label: 'Force Flash',
value: BuildJobType.ForceFlash,
},
]}
onButtonClick={(value: string | null) => {
if (value === BuildJobType.BuildAndFlash) {
onBuildAndFlash();
} else if (value === BuildJobType.ForceFlash) {
onForceFlash();
}
}}
/>
)}
</div>
</CardContent>
</Card>
<Card>
{networkDevices.size > 0 && (
<Box>
<Divider />
<CardTitle icon={<NetworkWifi />} title="Network Devices" />
<Divider />
<CardContent>
<div>
<WifiDeviceList
wifiDevices={Array.from(networkDevices.values())}
onChange={(dnsDevice: MulticastDnsInformation) => {
onDeviceChange(dnsDevice);
handleSelectedDeviceChange(dnsDevice.name);
}}
/>
</div>
</CardContent>
</Box>
)}
</Card>
<Dialog
open={deviceSelectErrorDialogOpen}
onClose={handleDeviceSelectErrorDialogClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
Device Select Error
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
The target device could not be automatically selected, it must
be done manually.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleDeviceSelectErrorDialogClose}>
Close
</Button>
</DialogActions>
</Dialog>
</>
)}
{viewState === ViewState.Compiling && (
<Card>
<CardTitle icon={<SettingsIcon />} title="Build" />
<Divider />
<CardContent>
<BuildProgressBar
inProgress={buildInProgress}
jobType={currentJobType}
progressNotification={lastProgressNotification}
/>
<BuildNotificationsList notifications={progressNotifications} />
<ShowAlerts severity="error" messages={buildFlashErrorResponse} />
</CardContent>
{logs.length > 0 && (
<>
<CardTitle
icon={<SettingsIcon />}
title={
<Box display="flex" justifyContent="space-between">
<Box>Logs</Box>
<Box>
<IconButton
aria-label="Copy log to clipboard"
title="Copy log to clipboard"
onClick={async () => {
await navigator.clipboard.writeText(logs);
}}
>
<ContentCopy />
</IconButton>
<IconButton
aria-label="Save log to file"
title="Save log to file"
onClick={saveBuildLogToFile}
>
<Save />
</IconButton>
</Box>
</Box>
}
/>
<Divider />
<CardContent>
<Box sx={styles.longBuildDurationWarning}>
<ShowTimeoutAlerts
severity="warning"
messages="Sometimes builds take at least a few minutes. It is normal, especially for the first time builds."
active={buildInProgress}
timeout={14 * 1000}
/>
</Box>
<Logs data={logs} />
</CardContent>
<Divider />
</>
)}
{response !== undefined && (
<>
<CardTitle icon={<SettingsIcon />} title="Result" />
<Divider />
<CardContent>
{response?.buildFlashFirmware?.success &&
currentJobType === BuildJobType.BuildAndFlash &&
deviceTarget?.flashingMethod === FlashingMethod.WIFI && (
<>
<Alert sx={styles.buildNotification} severity="warning">
<AlertTitle>Warning</AlertTitle>
Please wait for LED to resume blinking before
disconnecting power
</Alert>
</>
)}
<ShowAfterTimeout
timeout={
response?.buildFlashFirmware?.success &&
currentJobType === BuildJobType.BuildAndFlash &&
deviceTarget?.flashingMethod === FlashingMethod.WIFI
? 15000
: 1000
}
active={!buildInProgress}
>
<Box sx={styles.buildNotification}>
<BuildResponse
response={response?.buildFlashFirmware}
firmwareVersionData={firmwareVersionData}
/>
</Box>
{response?.buildFlashFirmware?.success && hasLuaScript && (
<>
<Alert sx={styles.buildNotification} severity="info">
<AlertTitle>Update Lua Script</AlertTitle>
Make sure to update the Lua script on your radio
</Alert>
</>
)}
</ShowAfterTimeout>
{response?.buildFlashFirmware?.success &&
currentJobType === BuildJobType.Build && (
<>
<Alert sx={styles.buildNotification} severity="info">
<AlertTitle>Build notice</AlertTitle>
{deviceTarget?.flashingMethod !== FlashingMethod.Radio
? 'Firmware binary file was opened in the file explorer'
: "Firmware binary file was opened in the file explorer, copy the firmware file to your radios's SD card and flash it to the transmitter using EdgeTX/OpenTX"}
</Alert>
</>
)}
</CardContent>
<Divider />
</>
)}
{!buildInProgress && (
<>
<CardTitle icon={<SettingsIcon />} title="Actions" />
<Divider />
<CardContent>
<Button
sx={styles.button}
color="primary"
size="large"
variant="contained"
onClick={onBack}
>
Back
</Button>
{!response?.buildFlashFirmware.success && (
<Button
sx={styles.button}
size="large"
variant="contained"
onClick={() => {
sendJob(currentJobType);
}}
>
Retry
</Button>
)}
{!response?.buildFlashFirmware.success &&
response?.buildFlashFirmware.errorType ===
BuildFirmwareErrorType.TargetMismatch && (
<Button
sx={styles.button}
size="large"
variant="contained"
onClick={onForceFlash}
>
Force Flash
</Button>
)}
{response?.buildFlashFirmware.success && luaDownloadButton()}
</CardContent>
</>
)}
</Card>
)}
</MainLayout>
);
}
Example #19
Source File: SessionList.tsx From abrechnung with GNU Affero General Public License v3.0 | 4 votes |
export default function SessionList() {
// TODO: fix editing functions
const [editedSessions, setEditedSessions] = useState({});
const [sessionToDelete, setSessionToDelete] = useState({
show: false,
toDelete: null,
});
const user = useRecoilValue(userData);
const sessions = user.sessions;
useTitle("Abrechnung - Sessions");
const editSession = (id) => {
if (!editedSessions.hasOwnProperty(id)) {
const newSessions = {
...editedSessions,
[id]: sessions.find((session) => session.id === id)?.name,
};
setEditedSessions(newSessions);
}
};
const stopEditSession = (id) => {
if (editedSessions.hasOwnProperty(id)) {
let newEditedSessions = { ...editedSessions };
delete newEditedSessions[id];
setEditedSessions(newEditedSessions);
}
};
const closeDeleteSessionModal = () => {
setSessionToDelete({ show: false, toDelete: null });
};
const performRename = (id) => {
if (editedSessions.hasOwnProperty(id)) {
renameSession({
sessionID: id,
name: editedSessions[id],
}).catch((err) => {
toast.error(err);
});
stopEditSession(id);
}
};
const openDeleteSessionModal = (id) => {
setSessionToDelete({ show: true, toDelete: id });
};
const confirmDeleteSession = () => {
if (sessionToDelete.toDelete !== null) {
deleteSession({ sessionID: sessionToDelete.toDelete }).catch((err) => {
toast.error(err);
});
setSessionToDelete({ show: false, toDelete: null });
}
};
const handleEditChange = (id, value) => {
const newEditedSessions = { ...editedSessions, [id]: value };
setEditedSessions(newEditedSessions);
};
const onKeyUp = (id) => (key) => {
if (key.keyCode === 13) {
performRename(id);
}
};
return (
<MobilePaper>
<Typography component="h3" variant="h5">
Login Sessions
</Typography>
<List>
{sessions.map((session) => {
if (editedSessions.hasOwnProperty(session.id)) {
return (
<ListItem key={session.id}>
<TextField
margin="normal"
variant="standard"
fullWidth
onKeyUp={onKeyUp(session.id)}
value={editedSessions[session.id]}
onChange={(event) => handleEditChange(session.id, event.target.value)}
/>
<ListItemSecondaryAction>
<Button onClick={() => performRename(session.id)}>
<Check />
</Button>
<Button onClick={() => stopEditSession(session.id)}>
<Close />
</Button>
</ListItemSecondaryAction>
</ListItem>
);
} else {
return (
<ListItem key={session.id}>
<ListItemText
primary={session.name}
secondary={
<>
<span>
Valid until{" "}
{DateTime.fromISO(session.valid_until).toLocaleString(
DateTime.DATETIME_FULL
) && "indefinitely"}
,{" "}
</span>
<span>
Last seen on{" "}
{DateTime.fromISO(session.last_seen).toLocaleString(
DateTime.DATETIME_FULL
)}
</span>
</>
}
/>
<ListItemSecondaryAction>
<IconButton onClick={() => editSession(session.id)}>
<Edit />
</IconButton>
<IconButton onClick={() => openDeleteSessionModal(session.id)}>
<Delete />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
);
}
})}
</List>
<Dialog open={sessionToDelete.show} onClose={closeDeleteSessionModal}>
<DialogTitle>Delete Session?</DialogTitle>
<DialogContent>
<DialogContentText>
{sessionToDelete.toDelete !== null
? `Are you sure you want to delete session ${
sessions.find((session) => session.id === sessionToDelete.toDelete)?.name
}`
: null}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button color="secondary" onClick={confirmDeleteSession}>
Yes pls
</Button>
<Button color="primary" onClick={closeDeleteSessionModal}>
No
</Button>
</DialogActions>
</Dialog>
</MobilePaper>
);
}
Example #20
Source File: CallOverlay.tsx From airmessage-web with Apache License 2.0 | 4 votes |
export default function CallOverlay() {
const displaySnackbar = useContext(SnackbarContext);
const existingTheme = useTheme();
const existingThemeMode = existingTheme.palette.mode;
//Invert theme
const theme = useMemo(() => {
return createTheme({
palette: {
mode: existingThemeMode === "light" ? "dark" : "light",
messageIncoming: undefined,
messageOutgoing: undefined,
messageOutgoingTextMessage: undefined
}
});
}, [existingThemeMode]);
//Subscribe to incoming caller updates
const [incomingCaller, setIncomingCaller] = useState<string | undefined>(undefined);
useEffect(() => {
ConnectionManager.incomingCallerEmitter.registerListener((caller) => {
//Update the caller state
setIncomingCaller(caller);
//Display a notification
getNotificationUtils().updateCallerNotification(caller);
});
return () => ConnectionManager.incomingCallerEmitter.unregisterListener(setIncomingCaller);
}, [setIncomingCaller]);
//Subscribe to outgoing callee updates
const [outgoingCallee, setOutgoingCallee] = useState<string[] | undefined>(undefined);
useEffect(() => {
ConnectionManager.outgoingCalleeEmitter.registerListener(setOutgoingCallee);
return () => ConnectionManager.outgoingCalleeEmitter.unregisterListener(setOutgoingCallee);
}, [setOutgoingCallee]);
const [outgoingCalleeReadable, setOutgoingCalleeReadable] = useState<string>("");
useEffect(() => {
//Ignore if there is no outgoing callee
if(outgoingCallee === undefined) {
setOutgoingCalleeReadable("");
return;
}
//Set a quick title by joining the member's addresses
setOutgoingCalleeReadable(buildListString(outgoingCallee));
//Look up the member's names to build the title
let invalidated = false;
getMemberTitle(outgoingCallee).then((title) => {
if(invalidated) return;
setOutgoingCalleeReadable(title);
});
return () => {
invalidated = true;
};
}, [outgoingCallee, setOutgoingCalleeReadable]);
//Set to true between the time that we have responded to an incoming call, and the server has yet to answer our message
const [incomingCallLoading, setIncomingCallLoading] = useState(false);
useEffect(() => {
//When the incoming caller changes, reset the loading state
setIncomingCallLoading(false);
}, [incomingCaller, setIncomingCallLoading]);
const declineIncomingCall = useCallback(() => {
setIncomingCallLoading(true);
ConnectionManager.handleIncomingFaceTimeCall(incomingCaller!, false);
}, [setIncomingCallLoading, incomingCaller]);
const acceptIncomingCall = useCallback(() => {
setIncomingCallLoading(true);
ConnectionManager.handleIncomingFaceTimeCall(incomingCaller!, true);
}, [setIncomingCallLoading, incomingCaller]);
const [outgoingCallLoading, setOutgoingCallLoading] = useState(false);
useEffect(() => {
//When the outgoing callee changes, reset the loading state
setOutgoingCallLoading(false);
}, [outgoingCallee, setOutgoingCallLoading]);
const cancelOutgoingCall = useCallback(() => {
setOutgoingCallLoading(true);
ConnectionManager.dropFaceTimeCallServer();
}, [setOutgoingCallLoading]);
const [errorDetailsDisplay, setErrorDetailsDisplay] = useState<string | undefined>(undefined);
//Subscribe to event updates
useEffect(() => {
const listener = (event: CallEvent) => {
switch(event.type) {
case "outgoingAccepted":
case "incomingHandled":
//Open the FaceTime link in a new tab
window.open(event.faceTimeLink, "_blank");
break;
case "outgoingError":
case "incomingHandleError":
//Let the user know that something went wrong
displaySnackbar({
message: "Your call couldn't be completed",
action: (
<Button onClick={() => setErrorDetailsDisplay(event.errorDetails)}>
Details
</Button>
),
});
break;
}
};
ConnectionManager.callEventEmitter.registerListener(listener);
return () => ConnectionManager.callEventEmitter.unregisterListener(listener);
}, [displaySnackbar, setErrorDetailsDisplay]);
return (<>
<ThemeProvider theme={theme}>
<Stack sx={{
position: "absolute",
top: 0,
right: 0,
padding: 1
}} spacing={1}>
{incomingCaller !== undefined && (
<CallNotificationIncoming
caller={incomingCaller}
onDecline={declineIncomingCall}
onAccept={acceptIncomingCall}
loading={incomingCallLoading} />
)}
{outgoingCallee !== undefined && (
<CallNotificationOutgoing
callee={outgoingCalleeReadable}
onCancel={cancelOutgoingCall}
loading={outgoingCallLoading} />
)}
</Stack>
</ThemeProvider>
<Dialog
open={errorDetailsDisplay !== undefined}
onClose={() => setErrorDetailsDisplay(undefined)}>
<DialogTitle>Call error details</DialogTitle>
<DialogContent>
<DialogContentText>
{errorDetailsDisplay}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => setErrorDetailsDisplay(undefined)} color="primary">
OK
</Button>
</DialogActions>
</Dialog>
</>);
}
Example #21
Source File: DeleteUser.tsx From console with GNU Affero General Public License v3.0 | 4 votes |
DeleteUser = ({
closeDeleteModalAndRefresh,
deleteOpen,
selectedUsers,
setErrorSnackMessage,
history,
}: IDeleteUserProps) => {
const onDelSuccess = () => closeDeleteModalAndRefresh(true);
const onDelError = (err: ErrorResponseHandler) => setErrorSnackMessage(err);
const onClose = () => closeDeleteModalAndRefresh(false);
const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError);
const [loadingSA, setLoadingSA] = useState<boolean>(true);
const [hasSA, setHasSA] = useState<boolean>(false);
const [userSAList, setUserSAList] = useState<userSACount[]>([]);
const userLoggedIn = localStorage.getItem("userLoggedIn") || "";
useEffect(() => {
if (selectedUsers) {
api
.invoke("POST", `/api/v1/users/service-accounts`, selectedUsers)
.then((res) => {
setUserSAList(res.userServiceAccountList);
if (res.hasSA) {
setHasSA(true);
}
setLoadingSA(false);
})
.catch((err: ErrorResponseHandler) => {
setErrorSnackMessage(err);
setLoadingSA(false);
});
}
}, [selectedUsers, setErrorSnackMessage]);
if (!selectedUsers) {
return null;
}
const renderUsers = selectedUsers.map((user) => (
<div key={user}>
<b>{user}</b>
</div>
));
const viewAction = (selectionElement: any): void => {
history.push(
`${IAM_PAGES.USERS}/${encodeURLString(selectionElement.userName)}`
);
onClose();
};
const tableActions = [
{
type: "view",
onClick: viewAction,
},
];
const onConfirmDelete = () => {
for (let user of selectedUsers) {
if (user === userLoggedIn) {
setErrorSnackMessage({
errorMessage: "Cannot delete currently logged in user",
detailedError: `Cannot delete currently logged in user ${userLoggedIn}`,
});
closeDeleteModalAndRefresh(true);
} else {
invokeDeleteApi("DELETE", `/api/v1/user/${encodeURLString(user)}`);
closeDeleteModalAndRefresh(true);
history.push(`${IAM_PAGES.USERS}`);
}
}
};
interface userSACount {
userName: string;
numSAs: number;
}
const noSAtext =
"Are you sure you want to delete the following " +
selectedUsers.length +
" " +
"user" +
(selectedUsers.length > 1 ? "s?" : "?");
return loadingSA ? (
<Loader />
) : (
<ConfirmDialog
title={`Delete User${selectedUsers.length > 1 ? "s" : ""}`}
confirmText={"Delete"}
isOpen={deleteOpen}
titleIcon={<ConfirmDeleteIcon />}
isLoading={deleteLoading}
onConfirm={onConfirmDelete}
onClose={onClose}
confirmationContent={
<DialogContentText>
{hasSA ? (
<Fragment>
<WarningMessage
label="Click on a user to view the full listing of asociated Service Accounts. All Service Accounts associated with a user will be deleted along with the user. Are you sure you want to continue?"
title="Warning: One or more users selected has associated Service Accounts. "
/>
<TableWrapper
itemActions={tableActions}
columns={[
{ label: "Username", elementKey: "userName" },
{
label: "# Associated Service Accounts",
elementKey: "numSAs",
},
]}
isLoading={loadingSA}
records={userSAList}
entityName="User Service Accounts"
idField="userName"
customPaperHeight="250"
/>
</Fragment>
) : (
<Fragment>
{noSAtext}
{renderUsers}
</Fragment>
)}
</DialogContentText>
}
/>
);
}
Example #22
Source File: index.tsx From material-table-formik with MIT License | 4 votes |
function FormikDialog<RowData extends IData>({
children,
onEditingCanceled,
validate,
onEditingApproved,
validationSchema,
mode,
editField: EditCell,
displayField: DisplayCell,
dialogLocalization,
dateTimePickerLocalization,
...props
}: IFormikDialogProps<RowData>) {
const { localization, data, columns } = props;
const mounted = useRef(false);
useEffect(() => {
mounted.current = true;
return () => {
mounted.current = false;
};
}, []);
const initialValues = React.useMemo(
() => (data ? { ...data } : ({} as RowData)),
[data]
);
const closeDialog = () => {
onEditingCanceled(mode, data);
};
let title;
switch (mode) {
case 'add':
title = dialogLocalization.addTooltip;
break;
case 'update':
title = dialogLocalization.editTooltip;
break;
case 'delete':
title = dialogLocalization.deleteHeader;
break;
}
const getEditCell = (
column: IColumn<RowData>,
field: FieldInputProps<RowData>,
meta: FieldMetaProps<RowData>,
setValues: (rowData: RowData) => void
) => {
if (!canEdit(column, mode, data)) {
if (column.render && data) {
return column.render(data, 'row');
} else {
return <div>{field.value}</div>;
}
}
const onChange = (newValue: string | number | boolean) =>
field.onChange({
target: {
value: newValue,
checked: newValue,
name: field.name,
},
});
const onRowDataChange = (newRowData: Partial<RowData>) => {
if (data) {
setValues({
...data,
...newRowData,
});
}
};
const errorProps: {
helperText?: string;
error: boolean;
} = {
helperText: meta.touched ? meta.error : '',
error: Boolean(meta.touched && meta.error !== undefined),
};
if (column.editComponent) {
return column.editComponent({
rowData: data || ({} as RowData),
value: field.value,
onChange,
onRowDataChange,
columnDef: (column as any) as EditCellColumnDef,
...errorProps,
});
} else {
return (
<EditCell
variant="standard"
{...field}
{...errorProps}
locale={dateTimePickerLocalization}
fullWidth={true}
id={column.field}
columnDef={column}
onChange={onChange}
rowData={data}
/>
);
}
};
return (
<>
<Dialog onClose={closeDialog} open={true} fullWidth={true}>
<DialogTitle id="simple-dialog-title">{title}</DialogTitle>
<Formik
validationSchema={validationSchema}
initialValues={initialValues}
validate={validate}
onSubmit={async (values, { setSubmitting }) => {
setSubmitting(true);
delete values.tableData;
await onEditingApproved(mode, values, data);
if (mounted && mounted.current) {
setSubmitting(false);
}
}}
>
{({ isSubmitting, handleSubmit, setValues }) => (
<form onSubmit={handleSubmit}>
<DialogContent>
<Grid container={true}>
{mode !== 'delete' &&
columns
.filter(column => isColumnVisible(column, mode))
.map(column => (
<Field key={column.field} name={column.field}>
{({ field, meta }: FieldAttributes<any>) => {
return (
<Grid
item={true}
xs={12}
{...column.gridProps}
sx={{ p: 1, ...(column.gridProps?.sx ?? {}) }}
>
<label htmlFor={column.field as string}>
{column.title}
</label>
<br />
{getEditCell(column, field, meta, setValues)}
</Grid>
);
}}
</Field>
))}
</Grid>
{mode === 'delete' && (
<DialogContentText>
{localization.deleteText}
</DialogContentText>
)}
<DialogActions>
{isSubmitting ? (
<CircularProgress size={25} />
) : (
<>
<Button onClick={closeDialog} color="primary">
{localization.cancelTooltip}
</Button>
<Button
color="primary"
autoFocus={true}
type="submit"
disabled={isSubmitting}
>
{mode !== 'delete'
? localization.saveTooltip
: dialogLocalization.deleteAction}
</Button>
</>
)}
</DialogActions>
</DialogContent>
</form>
)}
</Formik>
</Dialog>
{data && (
<MTableBodyRow {...props} onToggleDetailPanel={() => {}}>
{children}
</MTableBodyRow>
)}
</>
);
}
Example #23
Source File: Inspect.tsx From console with GNU Affero General Public License v3.0 | 4 votes |
Inspect = ({ classes }: { classes: any }) => {
const dispatch = useDispatch();
const distributedSetup = useSelector(selDistSet);
const [volumeName, setVolumeName] = useState<string>("");
const [inspectPath, setInspectPath] = useState<string>("");
const [isEncrypt, setIsEncrypt] = useState<boolean>(true);
const [decryptionKey, setDecryptionKey] = useState<string>("");
const [insFileName, setInsFileName] = useState<string>("");
const [isFormValid, setIsFormValid] = useState<boolean>(false);
const [volumeError, setVolumeError] = useState<string>("");
const [pathError, setPathError] = useState<string>("");
/**
* Validation Effect
*/
useEffect(() => {
let isVolValid;
let isPathValid;
isVolValid = volumeName.trim().length > 0;
if (!isVolValid) {
setVolumeError("This field is required");
} else if (volumeName.slice(0, 1) === "/") {
isVolValid = false;
setVolumeError("Volume/Bucket name cannot start with /");
}
isPathValid = inspectPath.trim().length > 0;
if (!inspectPath) {
setPathError("This field is required");
} else if (inspectPath.slice(0, 1) === "/") {
isPathValid = false;
setPathError("Path cannot start with /");
}
const isValid = isVolValid && isPathValid;
if (isVolValid) {
setVolumeError("");
}
if (isPathValid) {
setPathError("");
}
setIsFormValid(isValid);
}, [volumeName, inspectPath]);
const makeRequest = async (url: string) => {
return await fetch(url, { method: "GET" });
};
const performInspect = async () => {
const file = encodeURLString(inspectPath);
const volume = encodeURLString(volumeName);
const urlOfInspectApi = `/api/v1/admin/inspect?volume=${volume}&file=${file}&encrypt=${isEncrypt}`;
makeRequest(urlOfInspectApi)
.then(async (res) => {
if (!res.ok) {
const resErr: any = await res.json();
dispatch(
setErrorSnackMessage({
errorMessage: resErr.message,
detailedError: resErr.code,
})
);
}
const blob: Blob = await res.blob();
//@ts-ignore
const filename = res.headers.get("content-disposition").split('"')[1];
const decryptKey = getCookieValue(filename) || "";
performDownload(blob, filename);
setInsFileName(filename);
setDecryptionKey(decryptKey);
})
.catch((err) => {
dispatch(setErrorSnackMessage(err));
});
};
const resetForm = () => {
setVolumeName("");
setInspectPath("");
setIsEncrypt(true);
};
const onCloseDecKeyModal = () => {
deleteCookie(insFileName);
setDecryptionKey("");
resetForm();
};
return (
<Fragment>
<PageHeader label={"Inspect"} />
<PageLayout>
{!distributedSetup ? (
<DistributedOnly
iconComponent={<InspectMenuIcon />}
entity={"Inspect"}
/>
) : (
<Box
sx={{
display: "flex",
alignItems: "flex-start",
justifyContent: "flex-start",
border: "1px solid #eaeaea",
padding: {
lg: "40px",
xs: "15px",
},
flexWrap: "wrap",
gap: {
lg: "55px",
xs: "20px",
},
height: {
md: "calc(100vh - 120px)",
xs: "100%",
},
flexFlow: {
lg: "row",
xs: "column",
},
}}
>
<Box
sx={{
border: "1px solid #eaeaea",
flex: {
md: 2,
xs: 1,
},
width: {
lg: "auto",
xs: "100%",
},
padding: {
lg: "40px",
xs: "15px",
},
}}
>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
performInspect();
}}
>
<Box>
<InputBoxWrapper
id="inspect_volume"
name="inspect_volume"
extraInputProps={{
"data-test-id": "inspect_volume",
}}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setVolumeName(e.target.value);
}}
label="Volume or Bucket Name"
value={volumeName}
error={volumeError}
required
placeholder={"test-bucket"}
/>
</Box>
<Box
sx={{
marginTop: "15px",
}}
>
<InputBoxWrapper
id="inspect_path"
name="inspect_path"
extraInputProps={{
"data-test-id": "inspect_path",
}}
error={pathError}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setInspectPath(e.target.value);
}}
label="File or Path to inspect"
value={inspectPath}
required
placeholder={"test*/xl.meta"}
/>
</Box>
<Box
sx={{
marginTop: "25px",
}}
>
<FormSwitchWrapper
classes={{
inputLabel: classes.switchLabel,
}}
extraInputProps={{
"data-test-id": "inspect_encrypt",
}}
label="Encrypt"
indicatorLabels={["True", "False"]}
checked={isEncrypt}
value={"true"}
id="inspect_encrypt"
name="inspect_encrypt"
onChange={(e) => {
setIsEncrypt(!isEncrypt);
}}
/>
</Box>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
marginTop: "55px",
}}
>
<Button
sx={{
marginRight: "15px",
}}
type="button"
color="primary"
variant="outlined"
data-test-id="inspect-clear-button"
onClick={resetForm}
>
Clear
</Button>
<Button
type="submit"
variant="contained"
color="primary"
data-test-id="inspect-submit-button"
disabled={!isFormValid}
>
Inspect
</Button>
</Box>
</form>
</Box>
<Box
sx={{
flex: 1,
minWidth: {
md: "365px",
xs: "100%",
},
width: "100%",
}}
>
<HelpBox
title={""}
iconComponent={null}
help={
<Fragment>
<Box
sx={{
marginTop: "-25px",
fontSize: "16px",
fontWeight: 600,
display: "flex",
alignItems: "center",
justifyContent: "flex-start",
padding: "2px",
}}
>
<Box
sx={{
backgroundColor: "#07193E",
height: "15px",
width: "15px",
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "50%",
marginRight: "18px",
padding: "3px",
"& .min-icon": {
height: "11px",
width: "11px",
fill: "#ffffff",
},
}}
>
<InspectMenuIcon />
</Box>
Learn more about the Inspect feature
</Box>
<Box
sx={{
marginTop: "16px",
fontWeight: 600,
fontStyle: "italic",
fontSize: "14px",
}}
>
Examples:
</Box>
<Box
sx={{
display: "flex",
flexFlow: "column",
fontSize: "14px",
flex: "2",
"& .step-number": {
color: "#ffffff",
height: "25px",
width: "25px",
background: "#081C42",
marginRight: "10px",
textAlign: "center",
fontWeight: 600,
borderRadius: "50%",
},
"& .step-row": {
fontSize: "14px",
display: "flex",
marginTop: "15px",
marginBottom: "15px",
"&.step-text": {
fontWeight: 400,
},
"&:before": {
content: "' '",
height: "7px",
width: "7px",
backgroundColor: "#2781B0",
marginRight: "10px",
marginTop: "7px",
flexShrink: 0,
},
},
"& .code-block-container": {
flex: "1",
marginTop: "15px",
marginLeft: "35px",
"& input": {
color: "#737373",
},
},
"& .example-code-block label": {
display: "inline-block",
width: {
sm: "160px",
xs: "100%",
},
fontWeight: 600,
fontSize: "14px",
},
"& code": {
width: {
sm: "100px",
xs: "100%",
},
paddingLeft: "10px",
fontFamily: "monospace",
paddingRight: "10px",
paddingTop: "3px",
paddingBottom: "3px",
borderRadius: "2px",
border: "1px solid #eaeaea",
fontSize: "10px",
color: "#082146",
fontWeight: 500,
},
"& .spacer": {
marginBottom: "5px",
},
}}
>
<Box>
<Box className="step-row">
<div className="step-text">
To Download 'xl.meta' for a specific object from all
the drives in a zip file:
</div>
</Box>
<ExampleBlock
pathVal={`test*/xl.meta`}
volumeVal={`test-bucket`}
/>
</Box>
<Box>
<Box className="step-row">
<div className="step-text">
To Download all constituent parts for a specific
object, and optionally encrypt the downloaded zip:
</div>
</Box>
<ExampleBlock
pathVal={`test*/xl.meta`}
volumeVal={`test*/*/part.*`}
/>
</Box>
<Box>
<Box className="step-row">
<div className="step-text">
To Download recursively all objects at a prefix.
<br />
NOTE: This can be an expensive operation use it with
caution.
</div>
</Box>
<ExampleBlock
pathVal={`test*/xl.meta`}
volumeVal={`test/**`}
/>
</Box>
</Box>
<Box
sx={{
marginTop: "30px",
marginLeft: "15px",
fontSize: "14px",
}}
>
You can learn more at our{" "}
<a
href="https://github.com/minio/minio/tree/master/docs/debugging?ref=con"
target="_blank"
rel="noreferrer"
>
documentation
</a>
.
</Box>
</Fragment>
}
/>
</Box>
</Box>
)}
{decryptionKey ? (
<ModalWrapper
modalOpen={true}
title="Inspect Decryption Key"
onClose={onCloseDecKeyModal}
titleIcon={<PasswordKeyIcon />}
>
<DialogContentText component="div">
<Box>
This will be displayed only once. It cannot be recovered.
<br />
Use secure medium to share this key.
</Box>
<form
noValidate
onSubmit={() => {
return false;
}}
>
<KeyRevealer value={decryptionKey} />
</form>
</DialogContentText>
</ModalWrapper>
) : null}
</PageLayout>
</Fragment>
);
}
Example #24
Source File: Message.tsx From airmessage-web with Apache License 2.0 | 4 votes |
export default function Message(props: Props) {
//State
const [attachmentDataArray, setAttachmentDataArray] = useState<FileDownloadResult[]>([]);
const [dialogOpen, setDialogOpen] = useState<"error" | "rawError" | undefined>(undefined);
function closeDialog() {
setDialogOpen(undefined);
}
function openDialogError() {
setDialogOpen("error");
}
function openDialogRawError() {
setDialogOpen("rawError");
}
function copyRawErrorAndClose() {
navigator.clipboard.writeText(props.message.error!.detail!);
closeDialog();
}
//Getting the message information
const isOutgoing = props.message.sender === undefined;
const displayAvatar = !isOutgoing && !props.flow.anchorTop;
const displaySender = props.isGroupChat && displayAvatar;
const messageConfirmed = props.message.status !== MessageStatusCode.Unconfirmed;
//Building the message style
const theme = useTheme();
let colorPalette: PaletteColor;
if(isOutgoing) {
if(props.service === appleServiceAppleMessage) colorPalette = theme.palette.messageOutgoing;
else colorPalette = theme.palette.messageOutgoingTextMessage;
} else {
colorPalette = theme.palette.messageIncoming;
}
const messagePartPropsBase: Partial<MessagePartProps> = {
alignSelf: isOutgoing ? "flex-end" : "flex-start",
color: colorPalette.contrastText,
backgroundColor: colorPalette.main,
opacity: messageConfirmed ? opacityConfirmed : opacityUnconfirmed
};
//Splitting the modifiers for each message part
const stickerGroups = props.message.stickers.reduce((accumulator: {[index: number]: StickerItem[]}, item: StickerItem) => {
if(accumulator[item.messageIndex]) accumulator[item.messageIndex].push(item);
else accumulator[item.messageIndex] = [item];
return accumulator;
}, {});
const tapbackGroups = props.message.tapbacks.reduce((accumulator: {[index: number]: TapbackItem[]}, item: TapbackItem) => {
if(accumulator[item.messageIndex]) accumulator[item.messageIndex].push(item);
else accumulator[item.messageIndex] = [item];
return accumulator;
}, {});
//Adding the message text
const components: React.ReactNode[] = [];
if(props.message.text) {
const partProps: MessagePartProps = {
...messagePartPropsBase,
borderRadius: getBorderRadius(props.flow.anchorTop, props.flow.anchorBottom || props.message.attachments.length > 0, isOutgoing),
marginTop: 0
} as MessagePartProps;
const component = <MessageBubble key="messagetext" text={props.message.text!} index={0} partProps={partProps} stickers={stickerGroups[0]} tapbacks={tapbackGroups[0]} />;
components.push(component);
}
function onAttachmentData(attachmentIndex: number, shouldDownload: boolean, result: FileDownloadResult) {
if(shouldDownload) {
//Downloading the file
const attachment = props.message.attachments[attachmentIndex];
downloadArrayBuffer(result.data, result.downloadType ?? attachment.type, result.downloadName ?? attachment.name);
} else {
//Updating the data
const newArray = [...attachmentDataArray];
newArray[attachmentIndex] = result;
setAttachmentDataArray(newArray);
}
}
function downloadData(attachmentIndex: number, data: ArrayBuffer | Blob) {
const attachment = props.message.attachments[attachmentIndex];
if(data instanceof ArrayBuffer) {
downloadArrayBuffer(data, attachment.type, attachment.name);
} else {
downloadBlob(data, attachment.type, attachment.name);
}
}
/**
* Computes the file data to display to the user
*/
const getComputedFileData = useCallback((attachmentIndex: number): FileDisplayResult => {
const attachment = props.message.attachments[attachmentIndex];
const downloadData = attachmentDataArray[attachmentIndex];
if(downloadData === undefined) {
return {
data: attachment.data,
name: attachment.name,
type: attachment.type
};
} else return {
data: downloadData.data,
name: downloadData.downloadName ?? attachment.name,
type: downloadData.downloadType ?? attachment.type
};
}, [props.message.attachments, attachmentDataArray]);
//Adding the attachments
for(let i = 0; i < props.message.attachments.length; i++) {
const index = props.message.text ? i + 1 : i;
const attachment = props.message.attachments[i];
const partProps: MessagePartProps = {
...messagePartPropsBase,
borderRadius: getBorderRadius(props.flow.anchorTop || index > 0, props.flow.anchorBottom || i + 1 < props.message.attachments.length, isOutgoing),
marginTop: index > 0 ? marginLinked : undefined
} as MessagePartProps;
//Checking if the attachment has data
const attachmentData = getComputedFileData(i);
if(attachmentData.data !== undefined && isAttachmentPreviewable(attachmentData.type)) {
//Custom background color
const imagePartProps = {
...partProps,
backgroundColor: theme.palette.background.sidebar,
};
if(attachmentData.type.startsWith("image/")) {
components.push(<MessageAttachmentImage key={attachment.guid ?? attachment.localID} data={attachmentData.data} name={attachmentData.name} type={attachmentData.type} partProps={imagePartProps} stickers={stickerGroups[index]} tapbacks={tapbackGroups[index]} />);
}
} else {
//Adding a generic download attachment
components.push(<MessageAttachmentDownloadable
key={attachment.guid ?? attachment.localID}
data={attachmentData.data}
name={attachmentData.name}
type={attachmentData.type}
size={attachment.size}
guid={attachment.guid!}
onDataAvailable={(data) => onAttachmentData(i, !isAttachmentPreviewable(data.downloadType ?? attachment.type), data)}
onDataClicked={(data) => downloadData(i, data)}
partProps={partProps}
stickers={stickerGroups[index]}
tapbacks={tapbackGroups[index]} />);
}
}
const messageStyle: CSSProperties = {
marginTop: props.flow.anchorTop ? marginLinked : marginUnlinked,
};
//Initializing state
const [personData, setPersonData] = useState<PersonData | undefined>();
useEffect(() => {
if(!props.message.sender) return;
//Requesting contact data
findPerson(props.message.sender).then(setPersonData, console.warn);
}, [props.message.sender]);
//Building and returning the component
return (
<div className={styles.message} style={messageStyle}>
{props.flow.showDivider && <Typography className={styles.separator} variant="body2" color="textSecondary">{getTimeDivider(props.message.date)}</Typography>}
{displaySender && <Typography className={styles.labelSender} variant="caption" color="textSecondary">{personData?.name ?? props.message.sender}</Typography>}
<div className={styles.messageSplit}>
{<Avatar className={styles.avatar} src={personData?.avatar} style={displayAvatar ? {visibility: "visible", backgroundColor: colorFromContact(props.message.sender ?? "")} : {visibility: "hidden"}} />}
<div className={styles.messageParts}>
{components}
</div>
{props.message.progress && !props.message.error && <CircularProgress className={styles.messageProgress} size={24} variant={props.message.progress === -1 ? "indeterminate" : "determinate"} value={props.message.progress} />}
{props.message.error && <IconButton className={styles.messageError} style={{color: theme.palette.error.light}} size="small" onClick={openDialogError}>
<ErrorRounded />
</IconButton>}
<Dialog open={dialogOpen === "error"} onClose={closeDialog}>
<DialogTitle>Your message could not be sent</DialogTitle>
{props.message.error !== undefined && <React.Fragment>
<DialogContent>
<DialogContentText>{messageErrorToUserString(props.message.error!.code)}</DialogContentText>
</DialogContent>
<DialogActions>
{props.message.error!.detail && <Button onClick={openDialogRawError} color="primary">
Error details
</Button>}
<Button onClick={closeDialog} color="primary" autoFocus>
Dismiss
</Button>
</DialogActions>
</React.Fragment>}
</Dialog>
<Dialog open={dialogOpen === "rawError"} onClose={closeDialog}>
<DialogTitle>Error details</DialogTitle>
{props.message.error !== undefined && <React.Fragment>
<DialogContent>
<DialogContentText className={styles.rawErrorText}>{props.message.error.detail!}</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={copyRawErrorAndClose} color="primary">
Copy to clipboard
</Button>
<Button onClick={closeDialog} color="primary" autoFocus>
Dismiss
</Button>
</DialogActions>
</React.Fragment>}
</Dialog>
</div>
{props.showStatus && <Typography className={styles.labelStatus} variant="caption" color="textSecondary">{getStatusString(props.message)}</Typography>}
</div>
);
}
Example #25
Source File: AuthButton.tsx From multi-downloader-nx with MIT License | 4 votes |
AuthButton: React.FC = () => {
const snackbar = useSnackbar();
const [open, setOpen] = React.useState(false);
const [username, setUsername] = React.useState('');
const [password, setPassword] = React.useState('');
const [usernameError, setUsernameError] = React.useState(false);
const [passwordError, setPasswordError] = React.useState(false);
const messageChannel = React.useContext(messageChannelContext);
const [loading, setLoading] = React.useState(false);
const [error, setError] = React.useState<Error|undefined>(undefined);
const [authed, setAuthed] = React.useState(false);
const checkAuth = async () => {
console.log(await messageChannel?.checkToken());
setAuthed((await messageChannel?.checkToken())?.isOk ?? false);
}
React.useEffect(() => { checkAuth() }, []);
const handleSubmit = async () => {
if (!messageChannel)
throw new Error('Invalid state'); //The components to confirm only render if the messageChannel is not undefinded
if (username.trim().length === 0)
return setUsernameError(true);
if (password.trim().length === 0)
return setPasswordError(true);
setUsernameError(false);
setPasswordError(false);
setLoading(true);
const res = await messageChannel.auth({ username, password });
if (res.isOk) {
setOpen(false);
snackbar.enqueueSnackbar('Logged in', {
variant: 'success'
});
setUsername('');
setPassword('');
} else {
setError(res.reason);
}
await checkAuth();
setLoading(false);
}
return <Require value={messageChannel}>
<Dialog open={open}>
<Dialog open={!!error}>
<DialogTitle>Error during Authentication</DialogTitle>
<DialogContentText>
{error?.name}
{error?.message}
</DialogContentText>
<DialogActions>
<Button onClick={() => setError(undefined)}>Close</Button>
</DialogActions>
</Dialog>
<DialogTitle sx={{ flexGrow: 1 }}>Authentication</DialogTitle>
<DialogContent>
<DialogContentText>
Here, you need to enter your username (most likely your Email) and your password.<br />
These information are not stored anywhere and are only used to authenticate with the service once.
</DialogContentText>
<TextField
error={usernameError}
helperText={usernameError ? 'Please enter something before submiting' : undefined}
margin="dense"
id="username"
label="Username"
type="text"
fullWidth
variant="standard"
value={username}
onChange={(e) => setUsername(e.target.value)}
disabled={loading}
/>
<TextField
error={passwordError}
helperText={passwordError ? 'Please enter something before submiting' : undefined}
margin="dense"
id="password"
label="Password"
type="password"
fullWidth
variant="standard"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={loading}
/>
</DialogContent>
<DialogActions>
{loading && <CircularProgress size={30}/>}
<Button disabled={loading} onClick={() => setOpen(false)}>Close</Button>
<Button disabled={loading} onClick={() => handleSubmit()}>Authenticate</Button>
</DialogActions>
</Dialog>
<Button startIcon={authed ? <Check />: <Close />} variant="contained" onClick={() => setOpen(true)}>Authenticate</Button>
</Require>
}
Example #26
Source File: DeleteTenant.tsx From console with GNU Affero General Public License v3.0 | 4 votes |
DeleteTenant = ({
deleteOpen,
selectedTenant,
closeDeleteModalAndRefresh,
}: IDeleteTenant) => {
const dispatch = useDispatch();
const [retypeTenant, setRetypeTenant] = useState("");
const onDelSuccess = () => closeDeleteModalAndRefresh(true);
const onDelError = (err: ErrorResponseHandler) =>
dispatch(setErrorSnackMessage(err));
const onClose = () => closeDeleteModalAndRefresh(false);
const [deleteVolumes, setDeleteVolumes] = useState<boolean>(false);
const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError);
const onConfirmDelete = () => {
if (retypeTenant !== selectedTenant.name) {
setErrorSnackMessage({
errorMessage: "Tenant name is incorrect",
detailedError: "",
});
return;
}
invokeDeleteApi(
"DELETE",
`/api/v1/namespaces/${selectedTenant.namespace}/tenants/${selectedTenant.name}`,
{ delete_pvcs: deleteVolumes }
);
};
return (
<ConfirmDialog
title={`Delete Tenant`}
confirmText={"Delete"}
isOpen={deleteOpen}
titleIcon={<ConfirmDeleteIcon />}
isLoading={deleteLoading}
onConfirm={onConfirmDelete}
onClose={onClose}
confirmButtonProps={{
disabled: retypeTenant !== selectedTenant.name || deleteLoading,
}}
confirmationContent={
<DialogContentText>
{deleteVolumes && (
<Grid item xs={12}>
<WarningMessage
title={"WARNING"}
label={
"Delete Volumes: Data will be permanently deleted. Please proceed with caution."
}
/>
</Grid>
)}
To continue please type <b>{selectedTenant.name}</b> in the box.
<Grid item xs={12}>
<InputBoxWrapper
id="retype-tenant"
name="retype-tenant"
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
setRetypeTenant(event.target.value);
}}
label=""
value={retypeTenant}
/>
<br />
<FormSwitchWrapper
checked={deleteVolumes}
id={`delete-volumes`}
label={"Delete Volumes"}
name={`delete-volumes`}
onChange={() => {
setDeleteVolumes(!deleteVolumes);
}}
value={deleteVolumes}
/>
</Grid>
</DialogContentText>
}
/>
);
}
Example #27
Source File: ExportButton.tsx From firecms with MIT License | 4 votes |
export function ExportButton<M extends { [Key: string]: any }, UserType>({
schema,
schemaResolver,
path,
exportConfig
}: ExportButtonProps<M, UserType>
) {
const dataSource = useDataSource();
const context = useFireCMSContext();
const dataRef = useRef<Entity<M>[]>();
const additionalDataRef = useRef<Record<string, any>[]>();
const [dataLoading, setDataLoading] = React.useState<boolean>(false);
const [dataLoadingError, setDataLoadingError] = React.useState<Error | undefined>();
// If in the initial load, we get more than INITIAL_DOCUMENTS_LIMIT results
const [hasLargeAmountOfData, setHasLargeAmountOfData] = React.useState<boolean>(false);
// did the user agree to export a large amount of data
const [fetchLargeDataAccepted, setFetchLargeDataAccepted] = React.useState<boolean>(false);
const [open, setOpen] = React.useState(false);
const handleClickOpen = useCallback(() => {
setOpen(true);
}, [setOpen]);
const handleClose = useCallback(() => {
setOpen(false);
}, [setOpen]);
const doDownload = useCallback((data: Entity<M>[] | undefined,
additionalData: Record<string, any>[] | undefined,
schema: EntitySchema<M>,
schemaResolver: EntitySchemaResolver<M>,
path: string,
exportConfig: ExportConfig | undefined) => {
if (!data)
throw Error("Trying to perform export without loading data first");
const resolvedSchema = schemaResolver({});
downloadCSV(data, additionalData, resolvedSchema, path, exportConfig);
}, []);
useEffect(() => {
if (!open) return;
setDataLoading(true);
const updateEntities = async (entities: Entity<M>[]) => {
if (entities.length >= INITIAL_DOCUMENTS_LIMIT) {
setHasLargeAmountOfData(true);
}
const pendingDownload = dataRef.current && entities.length > dataRef.current.length && fetchLargeDataAccepted;
dataRef.current = entities;
const additionalColumnsData = await fetchAdditionalColumns(entities);
additionalDataRef.current = additionalColumnsData;
setDataLoading(false);
setDataLoadingError(undefined);
if (pendingDownload) {
doDownload(entities, additionalColumnsData, schema, schemaResolver, path, exportConfig);
handleClose();
}
};
const fetchAdditionalColumns = async (entities: Entity<M>[]) => {
if (!exportConfig?.additionalColumns) {
return;
}
const additionalColumns = exportConfig.additionalColumns;
const resolvedColumnsValues: Record<string, any>[] = await Promise.all(entities.map(async (entity) => {
return (await Promise.all(additionalColumns.map(async (column) => {
return {
[column.key]: await column.builder({
entity,
context
})
};
}))).reduce((a, b) => ({ ...a, ...b }), {});
}));
return resolvedColumnsValues;
};
const onFetchError = (error: Error) => {
console.error("ERROR", error);
setDataLoading(false);
setDataLoadingError(error);
};
dataSource.fetchCollection<M>({
path,
schema: schemaResolver,
limit: fetchLargeDataAccepted ? undefined : INITIAL_DOCUMENTS_LIMIT
})
.then(updateEntities)
.catch(onFetchError);
}, [path, fetchLargeDataAccepted, schema, open, dataSource, schemaResolver, doDownload, exportConfig, handleClose, context]);
const needsToAcceptFetchAllData = hasLargeAmountOfData && !fetchLargeDataAccepted;
const onOkClicked = useCallback(() => {
if (needsToAcceptFetchAllData) {
setFetchLargeDataAccepted(true);
} else {
doDownload(dataRef.current, additionalDataRef.current, schema, schemaResolver, path, exportConfig);
handleClose();
}
}, [needsToAcceptFetchAllData, doDownload, schema, schemaResolver, path, exportConfig, handleClose]);
return <>
<Tooltip title={"Export"}>
<IconButton color={"primary"} onClick={handleClickOpen}
size="large">
<GetAppIcon/>
</IconButton>
</Tooltip>
<Dialog
open={open}
onClose={handleClose}
>
<DialogTitle>Export data</DialogTitle>
<DialogContent>
<DialogContentText>
<div>Download the the content of this table as a CSV
</div>
<br/>
{needsToAcceptFetchAllData &&
<Alert elevation={1}
variant="filled"
severity={"warning"}>
<div>
This collections has a large number
of documents (more than {INITIAL_DOCUMENTS_LIMIT}).
</div>
<div>
Would you like to proceed?
</div>
</Alert>}
</DialogContentText>
</DialogContent>
<DialogActions>
{dataLoading && <CircularProgress size={16} thickness={8}/>}
<Button color="primary" onClick={handleClose}>
Cancel
</Button>
<Button color="primary"
disabled={dataLoading}
onClick={onOkClicked}>
Download
</Button>
</DialogActions>
</Dialog>
</>;
}
Example #28
Source File: InspectObject.tsx From console with GNU Affero General Public License v3.0 | 4 votes |
InspectObject = ({
classes,
closeInspectModalAndRefresh,
inspectOpen,
inspectPath,
volumeName,
}: IInspectObjectProps) => {
const dispatch = useDispatch();
const onClose = () => closeInspectModalAndRefresh(false);
const [isEncrypt, setIsEncrypt] = useState<boolean>(true);
const [decryptionKey, setDecryptionKey] = useState<string>("");
const [insFileName, setInsFileName] = useState<string>("");
if (!inspectPath) {
return null;
}
const makeRequest = async (url: string) => {
return await fetch(url, { method: "GET" });
};
const performInspect = async () => {
const file = encodeURLString(inspectPath + "/xl.meta");
const volume = encodeURLString(volumeName);
const urlOfInspectApi = `/api/v1/admin/inspect?volume=${volume}&file=${file}&encrypt=${isEncrypt}`;
makeRequest(urlOfInspectApi)
.then(async (res) => {
if (!res.ok) {
const resErr: any = await res.json();
dispatch(
setErrorSnackMessage({
errorMessage: resErr.message,
detailedError: resErr.code,
})
);
}
const blob: Blob = await res.blob();
//@ts-ignore
const filename = res.headers.get("content-disposition").split('"')[1];
const decryptKey = getCookieValue(filename) || "";
performDownload(blob, filename);
setInsFileName(filename);
if (decryptKey === "") {
onClose();
return;
}
setDecryptionKey(decryptKey);
})
.catch((err) => {
dispatch(setErrorSnackMessage(err));
});
};
const onCloseDecKeyModal = () => {
deleteCookie(insFileName);
onClose();
setDecryptionKey("");
};
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
};
return (
<React.Fragment>
{!decryptionKey && (
<ModalWrapper
modalOpen={inspectOpen}
titleIcon={<InspectMenuIcon />}
title={`Inspect Object`}
onClose={onClose}
>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
onSubmit(e);
}}
>
Would you like to encrypt <b>{decodeURLString(inspectPath)}</b>?{" "}
<br />
<FormSwitchWrapper
label={"Encrypt"}
indicatorLabels={["Yes", "No"]}
checked={isEncrypt}
value={"encrypt"}
id="encrypt"
name="encrypt"
onChange={(e) => {
setIsEncrypt(!isEncrypt);
}}
description=""
/>
<Grid item xs={12} className={classes.modalButtonBar}>
<Button
type="submit"
variant="contained"
color="primary"
onClick={performInspect}
>
Inspect
</Button>
</Grid>
</form>
</ModalWrapper>
)}
{decryptionKey ? (
<ModalWrapper
modalOpen={inspectOpen}
title="Inspect Decryption Key"
onClose={onCloseDecKeyModal}
titleIcon={<PasswordKeyIcon />}
>
<DialogContentText>
<Box>
This will be displayed only once. It cannot be recovered.
<br />
Use secure medium to share this key.
</Box>
<Box>
<KeyRevealer value={decryptionKey} />
</Box>
</DialogContentText>
</ModalWrapper>
) : null}
</React.Fragment>
);
}
Example #29
Source File: NewChannel.tsx From sapio-studio with Mozilla Public License 2.0 | 2 votes |
export function NewChannel(props: { show: boolean; hide: () => void }) {
const [value, set_value] = React.useState<null | string>(null);
return (
<Dialog onClose={props.hide} open={props.show}>
<DialogTitle>Create a new channel</DialogTitle>
<DialogContent>
<DialogContentText>
The name of the new channel to create...
</DialogContentText>
<TextField
onChange={(ev) => set_value(ev.currentTarget.value)}
value={value}
autoFocus
margin="dense"
label="Name"
name="name"
type="text"
fullWidth
variant="standard"
/>
</DialogContent>
<DialogActions>
<Button onClick={props.hide}>Cancel</Button>
<Button
color="success"
onClick={async (ev) => {
if (value !== null) {
window.electron.chat.send({
msg: { Data: 'hello' },
channel: value,
sent_time_ms: Date.now(),
});
props.hide();
}
props.hide();
}}
>
Create
</Button>
</DialogActions>
</Dialog>
);
}