@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 vote down vote up
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 vote down vote up
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 vote down vote up
/**
 * 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&apos;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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
/**
 * 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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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>
    );
}