@material-ui/core#ListSubheader TypeScript Examples

The following examples show how to use @material-ui/core#ListSubheader. 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: SettingsPage.tsx    From twitch-live-extension with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
SettingsPage = () => {
    const dispatch: AppDispatch = useDispatch();

    const [user, setUser] = useState<ValidateTokenResponse>();
    const { loading } = useSelector((state: RootState) => state.common);

    useEffect(() => {
        const getUserData = async () => {
            const userData: ValidateTokenResponse = await dispatch(getUser());

            setUser(userData);
        };

        getUserData();
    }, [dispatch]);

    return (
        <div>
            {user && (
                <List subheader={<ListSubheader>Settings</ListSubheader>}>
                    <SettingsSwitchAccount user={user} />
                    <SettingsNotifications />
                </List>
            )}
            {loading && !user && <CenteredCircularProgress />}
            <Divider />
            <SettingsFooter />
        </div>
    );
}
Example #2
Source File: Incidents.tsx    From backstage with Apache License 2.0 6 votes vote down vote up
Incidents = ({ serviceId, refreshIncidents }: Props) => {
  const api = useApi(pagerDutyApiRef);

  const [{ value: incidents, loading, error }, getIncidents] = useAsyncFn(
    async () => await api.getIncidentsByServiceId(serviceId),
  );

  useEffect(() => {
    getIncidents();
  }, [refreshIncidents, getIncidents]);

  if (error) {
    return (
      <Alert severity="error">
        Error encountered while fetching information. {error.message}
      </Alert>
    );
  }

  if (loading) {
    return <Progress />;
  }

  if (!incidents?.length) {
    return <IncidentsEmptyState />;
  }

  return (
    <List dense subheader={<ListSubheader>INCIDENTS</ListSubheader>}>
      {incidents!.map((incident, index) => (
        <IncidentListItem key={incident.id + index} incident={incident} />
      ))}
    </List>
  );
}
Example #3
Source File: ChangeEvents.tsx    From backstage with Apache License 2.0 6 votes vote down vote up
ChangeEvents = ({ serviceId, refreshEvents }: Props) => {
  const api = useApi(pagerDutyApiRef);

  const [{ value: changeEvents, loading, error }, getChangeEvents] = useAsyncFn(
    async () => await api.getChangeEventsByServiceId(serviceId),
  );

  useEffect(() => {
    getChangeEvents();
  }, [refreshEvents, getChangeEvents]);

  if (error) {
    return (
      <Alert severity="error">
        Error encountered while fetching information. {error.message}
      </Alert>
    );
  }

  if (loading) {
    return <Progress />;
  }

  if (!changeEvents?.length) {
    return <ChangeEventEmptyState />;
  }

  return (
    <List dense subheader={<ListSubheader>CHANGE EVENTS</ListSubheader>}>
      {changeEvents!.map((changeEvent, index) => (
        <ChangeEventListItem
          key={changeEvent.id + index}
          changeEvent={changeEvent}
        />
      ))}
    </List>
  );
}
Example #4
Source File: MenuContents.tsx    From firetable with Apache License 2.0 5 votes vote down vote up
export default function MenuContents({ menuItems }: IMenuContentsProps) {
  const classes = useStyles();

  return (
    <>
      {menuItems.map((item, index) => {
        if (item.type === "subheader")
          return (
            <>
              {index !== 0 && <Divider />}
              <ListSubheader
                key={index}
                className={classes.subheader}
                disableGutters
                disableSticky
              >
                {item.label}
              </ListSubheader>
            </>
          );

        let icon: JSX.Element = item.icon ?? <></>;
        if (item.active && !!item.activeIcon) icon = item.activeIcon;

        return (
          <>
            {index !== 0 && <Divider />}
            <MenuItem
              key={index}
              onClick={item.onClick}
              className={clsx(
                classes.menuItem,
                item.active && classes.menuItemActive,
                item.color === "error" && classes.menuItemError
              )}
              disabled={item.disabled}
            >
              <ListItemIcon className={classes.menuItemIcon}>
                {icon}
              </ListItemIcon>
              {item.active ? item.activeLabel : item.label}
            </MenuItem>
          </>
        );
      })}
    </>
  );
}
Example #5
Source File: TemplateDialog.tsx    From prompts-ai with MIT License 5 votes vote down vote up
export default function TemplateDialog() {
    const dispatch = useDispatch();
    const classes = useStyles();

    const templateDialogOpen = useSelector(selectTemplateDialogVisible);
    const handleTemplateDialogClose = () => {
        dispatch(toggleTemplateDialog(false));
    };

    const templateGroups = getTemplateGroups();
    const handleLoadTemplate = (template: Template) => () => {
        dispatch(loadTemplate(template.actionPayload))
        dispatch(cleanExampleList());
        handleTemplateDialogClose();
    };

    return <Dialog
        open={templateDialogOpen}
        onClose={handleTemplateDialogClose}
        aria-labelledby="template-dialog-title"
    >
        <DialogTitle id="template-dialog-title">Load Template</DialogTitle>
        <DialogContent
            className={classes.templateDialog}
        >
            {templateGroups.map((templateGroup, ind) => (
                <div key={ind}>
                    <List subheader={<ListSubheader className={classes.templateGroupHeader}>{templateGroup.name}</ListSubheader>}>
                        {templateGroup.templates.map(template => (
                            <ListItem key={template.id} button
                                      onClick={handleLoadTemplate(template)}><ListItemText>{template.name}</ListItemText></ListItem>
                        ))}
                    </List>
                </div>
            ))}

        </DialogContent>
        <DialogActions>
            <Button onClick={handleTemplateDialogClose} color="primary">
                Close
            </Button>
        </DialogActions>
    </Dialog>;
}
Example #6
Source File: Search.tsx    From ra-enterprise-demo with MIT License 5 votes vote down vote up
CustomSearchPanel: FC<SearchPanelProps> = props => {
    const listRef = useRef<HTMLUListElement>(
        null
    ) as React.MutableRefObject<HTMLUListElement>;
    const translate = useTranslate();
    const classes = useCustomSearchPanelStyles(props);
    useArrowKeysToNavigate(listRef);
    const { data, onClose } = useSearchResults();

    if (!data || data.length === 0) {
        return (
            <List data-testid="search-panel" dense {...props}>
                <ListItem>
                    <ListItemText
                        primary={translate('ra.navigation.no_results')}
                    />
                </ListItem>
            </List>
        );
    }
    const groupedData = groupSearchResultsByResource(data, translate);

    return (
        <List
            className={classes.root}
            data-testid="search-panel"
            dense
            innerRef={listRef}
            {...props}
        >
            {groupedData.map(group => (
                <Fragment key={group.label}>
                    <ListSubheader
                        role="presentation"
                        className={classes.header}
                        disableSticky
                    >
                        <>
                            <Typography
                                className={classes.headerGroup}
                                variant="subtitle1"
                            >
                                {translate(group.label.toString(), {
                                    _: group.label,
                                })}
                            </Typography>
                            <Typography
                                className={classes.headerCount}
                                variant="subtitle1"
                            >
                                {translate('ra-search.result', {
                                    smart_count: group.data.length,
                                })}
                            </Typography>
                        </>
                    </ListSubheader>
                    {group.data.map(searchResultItem => (
                        <CustomSearchResultItem
                            key={searchResultItem.id}
                            data={searchResultItem}
                            onClose={onClose}
                        />
                    ))}
                </Fragment>
            ))}
        </List>
    );
}
Example #7
Source File: Incidents.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
Incidents = ({ readOnly, refreshIncidents, team }: Props) => {
  const classes = useStyles();
  const api = useApi(splunkOnCallApiRef);

  const [{ value: incidents, loading, error }, getIncidents] = useAsyncFn(
    async () => {
      // For some reason the changes applied to incidents (trigger-resolve-acknowledge)
      // may take some time to actually be applied after receiving the response from the server.
      // The timeout compensates for this latency.
      await new Promise(resolve => setTimeout(resolve, 2000));
      const allIncidents = await api.getIncidents();
      const teams = await api.getTeams();
      const teamSlug = teams.find(teamValue => teamValue.name === team)?.slug;
      const filteredIncidents = teamSlug
        ? allIncidents.filter(incident =>
            incident.pagedTeams?.includes(teamSlug),
          )
        : [];
      return filteredIncidents;
    },
  );

  useEffect(() => {
    getIncidents();
  }, [refreshIncidents, getIncidents]);

  if (error) {
    return (
      <Alert severity="error">
        Error encountered while fetching information. {error.message}
      </Alert>
    );
  }

  if (!loading && !incidents?.length) {
    return <IncidentsEmptyState />;
  }

  return (
    <List
      className={classes.root}
      dense
      subheader={
        <ListSubheader className={classes.subheader}>
          CRITICAL INCIDENTS
        </ListSubheader>
      }
    >
      {loading ? (
        <Progress className={classes.progress} />
      ) : (
        incidents!.map((incident, index) => (
          <IncidentListItem
            onIncidentAction={() => getIncidents()}
            key={index}
            team={team}
            incident={incident}
            readOnly={readOnly}
          />
        ))
      )}
    </List>
  );
}
Example #8
Source File: EscalationPolicy.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
EscalationPolicy = ({ users, team }: Props) => {
  const classes = useStyles();
  const api = useApi(splunkOnCallApiRef);

  const {
    value: userNames,
    loading,
    error,
  } = useAsync(async () => {
    const oncalls = await api.getOnCallUsers();
    const teamUsernames = oncalls
      .filter(oncall => oncall.team?.name === team)
      .flatMap(oncall => {
        return oncall.oncallNow?.flatMap(oncallNow => {
          return oncallNow.users?.flatMap(user => {
            return user?.onCalluser?.username;
          });
        });
      });
    return teamUsernames;
  });

  if (error) {
    return (
      <Alert severity="error">
        Error encountered while fetching information. {error.message}
      </Alert>
    );
  }

  if (!loading && !userNames?.length) {
    return <EscalationUsersEmptyState />;
  }

  return (
    <List
      className={classes.root}
      dense
      subheader={
        <ListSubheader className={classes.subheader}>ON CALL</ListSubheader>
      }
    >
      {loading ? (
        <Progress className={classes.progress} />
      ) : (
        userNames &&
        userNames.map(
          (userName, index) =>
            userName &&
            userName in users && (
              <EscalationUser key={index} user={users[userName]} />
            ),
        )
      )}
    </List>
  );
}
Example #9
Source File: EscalationPolicy.tsx    From backstage with Apache License 2.0 5 votes vote down vote up
EscalationPolicy = ({ policyId }: Props) => {
  const api = useApi(pagerDutyApiRef);

  const {
    value: users,
    loading,
    error,
  } = useAsync(async () => {
    const oncalls = await api.getOnCallByPolicyId(policyId);
    const usersItem = oncalls
      .sort((a, b) => a.escalation_level - b.escalation_level)
      .map(oncall => oncall.user);

    return usersItem;
  });

  if (error) {
    return (
      <Alert severity="error">
        Error encountered while fetching information. {error.message}
      </Alert>
    );
  }

  if (loading) {
    return <Progress />;
  }

  if (!users?.length) {
    return <EscalationUsersEmptyState />;
  }

  return (
    <List dense subheader={<ListSubheader>ON CALL</ListSubheader>}>
      {users!.map((user, index) => (
        <EscalationUser key={index} user={user} />
      ))}
    </List>
  );
}
Example #10
Source File: BoundaryDropdown.tsx    From prism-frontend with MIT License 5 votes vote down vote up
ClickableListSubheader = styled(ListSubheader)(({ theme }) => ({
  // Override the default list subheader style to make it clickable
  pointerEvents: 'inherit',
  cursor: 'pointer',
  '&:hover': {
    backgroundColor: theme.palette.grey[100],
  },
}))
Example #11
Source File: TransactionList.tsx    From End-to-End-Web-Testing-with-Cypress with MIT License 5 votes vote down vote up
TransactionList: React.FC<TransactionListProps> = ({
  header,
  transactions,
  isLoading,
  showCreateButton,
  loadNextPage,
  pagination,
  filterComponent,
}) => {
  const classes = useStyles();

  const showEmptyList = !isLoading && transactions?.length === 0;
  const showSkeleton = isLoading && isEmpty(pagination);

  return (
    <Paper className={classes.paper}>
      {filterComponent}
      <ListSubheader component="div">{header}</ListSubheader>
      {showSkeleton && <SkeletonList />}
      {transactions.length > 0 && (
        <TransactionInfiniteList
          transactions={transactions}
          loadNextPage={loadNextPage}
          pagination={pagination}
        />
      )}
      {showEmptyList && (
        <EmptyList entity="Transactions">
          <Grid
            container
            direction="column"
            justify="center"
            alignItems="center"
            style={{ width: "100%" }}
            spacing={2}
          >
            <Grid item>
              <TransferMoneyIllustration style={{ height: 200, width: 300, marginBottom: 30 }} />
            </Grid>
            <Grid item>
              {showCreateButton && (
                <Button
                  data-test="transaction-list-empty-create-transaction-button"
                  variant="contained"
                  color="primary"
                  component={RouterLink}
                  to="/transaction/new"
                >
                  Create A Transaction
                </Button>
              )}
            </Grid>
          </Grid>
        </EmptyList>
      )}
    </Paper>
  );
}
Example #12
Source File: NavDrawer.tsx    From akashlytics with GNU General Public License v3.0 5 votes vote down vote up
export function NavDrawer({ isDrawerOpen, toggleDrawer }) {
  const classes = useStyles();
  const anchor = "left";

  return (
    <React.Fragment>
      <SwipeableDrawer anchor={anchor} open={isDrawerOpen} onClose={toggleDrawer(false)} onOpen={toggleDrawer(true)}>
        <div className={clsx(classes.list)} role="presentation" onClick={toggleDrawer(false)} onKeyDown={toggleDrawer(false)}>
          <List
            subheader={
              <ListSubheader component={Link} to="/" id="nested-list-subheader" className={classes.listSubHeader}>
                <img src="/images/akashlytics_logo_compact_small.png" alt="Akashlytics logo" className={clsx(classes.listSubHeaderLogo, "App-logo")} />
              </ListSubheader>
            }
          >
            <ListItem button component={Link} to="/">
              <ListItemIcon>
                <DashboardIcon />
              </ListItemIcon>
              <ListItemText primary="Dashboard" />
            </ListItem>
            <ListItem button component={Link} to="/price-compare">
              <ListItemIcon>
                <AttachMoneyIcon />
              </ListItemIcon>
              <ListItemText primary="Compare price" />
            </ListItem>
            <ListItem button component={Link} to="/faq">
              <ListItemIcon>
                <HelpIcon />
              </ListItemIcon>
              <ListItemText primary="FAQ" />
            </ListItem>
            <ListItem button component={Link} to="/deploy">
              <ListItemIcon>
                <CloudUploadIcon />
              </ListItemIcon>
              <ListItemText primary="Deploy" />
            </ListItem>
          </List>
        </div>
      </SwipeableDrawer>
    </React.Fragment>
  );
}
Example #13
Source File: LayerDropdown.tsx    From prism-frontend with MIT License 4 votes vote down vote up
function LayerDropdown({
  type,
  value,
  setValue,
  placeholder,
  ...rest
}: LayerSelectorProps) {
  // this could be testable, needs to be constructed in a way that prevents it breaking whenever new layers are added. (don't put layer name in snapshot)

  const { t } = useSafeTranslation();

  const categories = menuList // we could memo this but it isn't impacting performance, for now
    // 1. flatten to just the layer categories, don't need the big menus
    .flatMap(menu => menu.layersCategories)
    // 2. breakdown grouped layer back into flat list of layers if activate_all = false
    .map(layerCategory => {
      if (layerCategory.layers.some(f => f.group)) {
        const layers = layerCategory.layers.map(layer => {
          if (layer.group && !layer.group.activateAll) {
            return layer.group.layers.map(layerKey => {
              return LayerDefinitions[layerKey.id as LayerKey];
            });
          }
          return layer;
        });
        return {
          title: layerCategory.title,
          layers: layers.flat(),
          tables: layerCategory.tables,
        };
      }
      return layerCategory;
    })
    // 3. get rid of layers within the categories which don't match the given type
    .map(category => ({
      ...category,
      layers: category.layers.filter(layer =>
        layer.type === 'wms'
          ? layer.type === type &&
            [undefined, 'polygon'].includes(layer.geometry)
          : layer.type === type,
      ),
    }))
    // 4. filter categories which don't have any layers at the end of it all.
    .filter(category => category.layers.length > 0);
  const defaultValue = 'placeholder';

  return (
    <FormControl {...rest}>
      <Select
        defaultValue={defaultValue}
        value={value}
        onChange={e => {
          setValue(e.target.value as LayerKey);
        }}
      >
        {categories.reduce(
          // map wouldn't work here because <Select> doesn't support <Fragment> with keys, so we need one array
          (components, category) => [
            ...components,
            <ListSubheader key={category.title}>
              <Typography variant="body2" color="primary">
                {t(category.title)}
              </Typography>
            </ListSubheader>,
            ...category.layers.map(layer => (
              <MenuItem key={layer.id} value={layer.id}>
                {t(layer.title || '')}
                {getLayerGeometryIcon(layer)}
              </MenuItem>
            )),
          ],
          (placeholder
            ? [
                <MenuItem key={defaultValue} value={defaultValue} disabled>
                  {t(placeholder)}
                </MenuItem>,
              ]
            : []) as ReactElement[],
        )}
      </Select>
    </FormControl>
  );
}
Example #14
Source File: Menu.tsx    From clarity with Apache License 2.0 4 votes vote down vote up
MoreMenu = observer((props: Props) => {
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);

  const classes = useStyles();

  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setTimeout(() => {
      setAnchorEl(null);
    }, 200);
  };

  return (
    <div>
      <IconButton
        edge="end"
        aria-controls="simple-menu"
        aria-haspopup="true"
        onClick={handleClick}
      >
        <MenuIcon />
      </IconButton>
      <Menu
        id={'simple-menu'}
        anchorEl={anchorEl}
        transformOrigin={{ vertical: 'top', horizontal: 'right' }}
        open={Boolean(anchorEl)}
        onClose={handleClose}
      >
        <List
          aria-labelledby="nested-list-subheader"
          subheader={
            <ListSubheader component="div" id="nested-list-subheader">
              Accounts
            </ListSubheader>
          }
        >
          {props.authContainer.userAccounts.map((account, i) => {
            return (
              <ListItem
                key={i}
                button
                dense={true}
                onClick={() => {
                  props.authContainer.switchToAccount(account.name);
                  handleClose();
                }}
              >
                {account.name ===
                props.authContainer.selectedUserAccount?.name ? (
                  <CheckIcon fontSize={'small'} />
                ) : (
                  <Icon className={'fa fa-fw'} fontSize={'small'} />
                )}
                <ListItemText primary={account.name} />
              </ListItem>
            );
          })}
          <Divider light />
          {props.authContainer.userAccounts.length > 0 && (
            <ListItem
              dense={true}
              component={Link}
              to={Pages.AccountManagement}
              button
              onClick={handleClose}
            >
              <SettingsIcon className={classes.menuIcon} />
              <ListItemText primary="Key Management" />
            </ListItem>
          )}
          {props.authContainer.selectedUserAccount && (
            <ListItem
              dense={true}
              button
              onClick={() => {
                props.authContainer.downloadActiveKey();
                handleClose();
              }}
            >
              <CloudDownloadIcon className={classes.menuIcon} />
              <ListItemText primary="Download Active Key" />
            </ListItem>
          )}
          <ListItem
            dense={true}
            button
            onClick={() => {
              props.authContainer.lock();
              handleClose();
            }}
          >
            <LockIcon className={classes.menuIcon} />
            <ListItemText primary="Lock" />
          </ListItem>
        </List>
      </Menu>
    </div>
  );
})
Example #15
Source File: AccountManagementPage.tsx    From signer with Apache License 2.0 4 votes vote down vote up
render() {
    return !this.props.accountManager.isUnLocked ||
      !this.props.accountManager.userAccounts[0] ? (
      <Redirect to={Pages.Home} />
    ) : (
      <React.Fragment>
        <DragDropContext onDragEnd={result => this.onDragEnd(result)}>
          <Droppable droppableId="droppable">
            {(provided, snapshot) => (
              <Observer>
                {() => (
                  // TODO: fix this (deprecated RootRef)
                  <RootRef rootRef={provided.innerRef}>
                    <List>
                      {this.props.accountManager.userAccounts.map(
                        (item, index) => (
                          <Draggable
                            key={item.alias}
                            draggableId={item.alias}
                            index={index}
                          >
                            {(provided, snapshot) => (
                              <ListItem
                                innerRef={provided.innerRef}
                                ContainerProps={{
                                  ...provided.draggableProps,
                                  ...provided.dragHandleProps,
                                  style: getItemStyle(
                                    snapshot.isDragging,
                                    provided.draggableProps.style
                                  )
                                }}
                              >
                                <ListItemText primary={item.alias} />
                                <ListItemSecondaryAction>
                                  <Tooltip title="Edit">
                                    <IconButton
                                      aria-label="Button will open a dialog to rename key"
                                      edge={'end'}
                                      onClick={() => {
                                        this.handleClickOpen(item);
                                      }}
                                    >
                                      <EditIcon />
                                    </IconButton>
                                  </Tooltip>
                                  <Tooltip title="Delete">
                                    <IconButton
                                      edge={'end'}
                                      onClick={() => {
                                        this.handleClickRemove(item.alias);
                                      }}
                                    >
                                      <DeleteIcon />
                                    </IconButton>
                                  </Tooltip>
                                  <Tooltip title="View">
                                    <IconButton
                                      edge={'end'}
                                      onClick={() => {
                                        this.handleViewKey(item.alias);
                                      }}
                                    >
                                      <VpnKeyIcon />
                                    </IconButton>
                                  </Tooltip>
                                  <Tooltip title="Download">
                                    <IconButton
                                      edge={'end'}
                                      onClick={() => {
                                        this.handleDownloadKeys(item.alias);
                                      }}
                                    >
                                      <GetApp />
                                    </IconButton>
                                  </Tooltip>
                                </ListItemSecondaryAction>
                              </ListItem>
                            )}
                          </Draggable>
                        )
                      )}
                      {provided.placeholder}
                    </List>
                  </RootRef>
                )}
              </Observer>
            )}
          </Droppable>
        </DragDropContext>
        <Dialog
          open={this.state.openDialog}
          onClose={this.handleClose}
          aria-label="Form to rename account - focus will be given to name input field"
          aria-labelledby="form-dialog-title"
        >
          <form>
            <DialogTitle id="form-dialog-title">Rename</DialogTitle>
            <DialogContent>
              <TextFieldWithFormState
                autoFocus
                fullWidth
                label="Rename account"
                placeholder="Account alias"
                id="rename-account"
                fieldState={this.renameAccountForm.name}
              />
            </DialogContent>
            <DialogActions>
              <Button onClick={this.handleClose} color="primary">
                Cancel
              </Button>
              <Button
                type="submit"
                onClick={this.handleUpdateName}
                color="primary"
                disabled={this.renameAccountForm.submitDisabled}
              >
                Update
              </Button>
            </DialogActions>
          </form>
        </Dialog>

        <Dialog
          fullScreen
          open={this.state.openKeyDialog}
          onClose={this.handleClose}
          aria-labelledby="form-dialog-title"
        >
          <DialogTitle id="form-dialog-title">Account Details</DialogTitle>
          <DialogContent>
            <List>
              <ListSubheader>
                <Typography variant={'h6'}>{this.state.alias}</Typography>
              </ListSubheader>
              <ListItem>
                <IconButton
                  edge={'start'}
                  onClick={() => {
                    copy(this.state.publicKeyHex!);
                    this.setState({ copyStatus: true });
                  }}
                >
                  <FilterNoneIcon />
                </IconButton>
                <ListItemText
                  primary={`Public Key: ${this.state.publicKeyHex}`}
                  style={{ overflowWrap: 'break-word' }}
                />
              </ListItem>
              <ListItem>
                <IconButton
                  edge={'start'}
                  onClick={() => {
                    copy(this.state.accountHash!);
                    this.setState({ copyStatus: true });
                  }}
                >
                  <FilterNoneIcon />
                </IconButton>
                <ListItemText
                  primary={`Account Hash: ${this.state.accountHash}`}
                  style={{ overflowWrap: 'break-word' }}
                />
              </ListItem>
            </List>
            <Snackbar
              open={this.state.copyStatus}
              message="Copied!"
              autoHideDuration={1500}
              onClose={this.handleCopyMessage}
            />
          </DialogContent>
          <DialogActions>
            <Button onClick={this.handleClose} color="primary">
              Close
            </Button>
          </DialogActions>
        </Dialog>
      </React.Fragment>
    );
  }
Example #16
Source File: Menu.tsx    From signer with Apache License 2.0 4 votes vote down vote up
MoreMenu = observer((props: Props) => {
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);

  const classes = useStyles();

  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setTimeout(() => {
      setAnchorEl(null);
    }, 200);
  };

  return (
    <div>
      <IconButton
        edge="end"
        aria-controls="simple-menu"
        aria-haspopup="true"
        onClick={handleClick}
        style={{ color: '#C4C4C4' }}
      >
        <MenuIcon />
      </IconButton>
      <Menu
        id={'simple-menu'}
        anchorEl={anchorEl}
        transformOrigin={{ vertical: 'top', horizontal: 'right' }}
        open={Boolean(anchorEl)}
        onClose={handleClose}
      >
        <List
          aria-labelledby="nested-list-subheader"
          subheader={
            <ListSubheader component="div" id="nested-list-subheader">
              {props.accountManager.userAccounts.length > 0
                ? 'Accounts'
                : 'No Account'}
            </ListSubheader>
          }
        >
          {props.accountManager.userAccounts.map((account, i) => {
            return (
              <ListItem
                key={i}
                button
                dense={true}
                onClick={() => {
                  props.accountManager.switchToAccount(account.alias);
                  handleClose();
                }}
              >
                {account.alias ===
                props.accountManager.activeUserAccount?.alias ? (
                  <CheckIcon fontSize={'small'} />
                ) : (
                  <Icon className={'fa fa-fw'} fontSize={'small'} />
                )}
                <ListItemText primary={account.alias} />
              </ListItem>
            );
          })}
          <Divider light />
          {props.accountManager.userAccounts.length > 0 && (
            <ListItem
              dense={true}
              component={Link}
              to={Pages.AccountManagement}
              button
              onClick={handleClose}
            >
              <SettingsIcon className={classes.menuIcon} />
              <ListItemText primary="Key Management" />
            </ListItem>
          )}
          <ListItem
            dense={true}
            component={Link}
            to={Pages.ConnectedSites}
            button
            onClick={handleClose}
          >
            <WebIcon className={classes.menuIcon} />
            <ListItemText primary="Connected Sites" />
          </ListItem>
          {props.accountManager.activeUserAccount && (
            <ListItem
              dense={true}
              button
              onClick={() => {
                props.accountManager.downloadActiveKey();
                handleClose();
              }}
            >
              <CloudDownloadIcon className={classes.menuIcon} />
              <ListItemText primary="Download Active Key" />
            </ListItem>
          )}
          <ListItem
            dense={true}
            component={Link}
            to={Pages.ConfigureTimeout}
            button
            onClick={handleClose}
          >
            <TimerIcon className={classes.menuIcon} />
            <ListItemText primary="Timeout" />
            <Typography variant="overline">
              {props.accountManager.idleTimeoutMins} min
              {props.accountManager.idleTimeoutMins === 1 ? '' : 's'}
            </Typography>
          </ListItem>
          <ListItem
            dense={true}
            button
            onClick={() => {
              props.accountManager.lock();
              handleClose();
            }}
          >
            <LockIcon className={classes.menuIcon} />
            <ListItemText primary="Lock" />
          </ListItem>
        </List>
      </Menu>
    </div>
  );
})
Example #17
Source File: LogIn.tsx    From clearflask with Apache License 2.0 4 votes vote down vote up
render() {
    if (!this.props.open && !this.props.inline) return null;

    const onboarding = this.props.config?.users.onboarding || this.props.onboardBefore;

    const notifOpts: Set<NotificationType> = new Set();
    const oauthOpts: Array<Client.NotificationMethodsOauth> = onboarding?.notificationMethods.oauth || [];
    if (onboarding) {
      // if (onboarding.notificationMethods.mobilePush === true
      //   && (this.props.overrideMobileNotification || MobileNotification.getInstance()).canAskPermission()) {
      //   switch ((this.props.overrideMobileNotification || MobileNotification.getInstance()).getDevice()) {
      //     case Device.Android:
      //       notifOpts.add(NotificationType.Android);
      //       break;
      //     case Device.Ios:
      //       notifOpts.add(NotificationType.Ios);
      //       break;
      //   }
      // }
      if (onboarding.notificationMethods.browserPush === true
        && (this.props.overrideWebNotification || WebNotification.getInstance()).canAskPermission()) {
        notifOpts.add(NotificationType.Browser);
      }
      if (onboarding.notificationMethods.anonymous
        && (onboarding.notificationMethods.anonymous.onlyShowIfPushNotAvailable !== true
          || (!notifOpts.has(NotificationType.Android) && !notifOpts.has(NotificationType.Ios) && !notifOpts.has(NotificationType.Browser)))) {
        notifOpts.add(NotificationType.Silent)
      }
      if (onboarding.notificationMethods.email) {
        notifOpts.add(NotificationType.Email);
      }
      if (onboarding.notificationMethods.sso) {
        notifOpts.add(NotificationType.SSO);
      }
      if (oauthOpts.length > 0) {
        notifOpts.add(NotificationType.OAuth);
      }
    }

    const signupAllowed = notifOpts.size > 0;
    const onlySingleOption = notifOpts.size === 1 && oauthOpts.length <= 1;
    const singleColumnLayout = this.props.fullScreen || onlySingleOption;

    const selectedNotificationType = (this.state.notificationType && notifOpts.has(this.state.notificationType))
      ? this.state.notificationType
      : (onlySingleOption ? notifOpts.values().next().value : undefined);
    const selectedOauthType = selectedNotificationType === NotificationType.OAuth && (this.state.oauthType
      ? this.state.oauthType
      : oauthOpts[0]?.oauthId);

    const emailValid = this.isEmailValid(this.state.email);
    const emailAllowedDomain = this.isAllowedDomain(this.state.email);
    const showDisplayNameInput = signupAllowed && !!onboarding?.accountFields && onboarding.accountFields.displayName !== Client.AccountFieldsDisplayNameEnum.None && selectedNotificationType !== NotificationType.SSO && selectedNotificationType !== NotificationType.OAuth;
    const isDisplayNameRequired = showDisplayNameInput && onboarding?.accountFields?.displayName === Client.AccountFieldsDisplayNameEnum.Required;
    const showPasswordInput = onboarding?.notificationMethods.email && onboarding.notificationMethods.email.password !== Client.EmailSignupPasswordEnum.None;
    const isPasswordRequired = onboarding?.notificationMethods.email && onboarding.notificationMethods.email.password === Client.EmailSignupPasswordEnum.Required;
    const showAccountFields = showPasswordInput || showDisplayNameInput;
    const showEmailInput = selectedNotificationType === NotificationType.Email;
    const showEmailInputInline = !showAccountFields;
    const isSubmittable = selectedNotificationType
      && (selectedNotificationType !== NotificationType.SSO)
      && (selectedNotificationType !== NotificationType.Android || this.state.notificationDataAndroid)
      && (selectedNotificationType !== NotificationType.Ios || this.state.notificationDataIos)
      && (selectedNotificationType !== NotificationType.Browser || this.state.notificationDataBrowser)
      && (!isDisplayNameRequired || this.state.displayName)
      && (selectedNotificationType !== NotificationType.Email || (emailValid && emailAllowedDomain))
      && (!isPasswordRequired || this.state.pass);

    const onlySingleOptionRequiresAllow = onlySingleOption &&
      ((selectedNotificationType === NotificationType.Android && !this.state.notificationDataAndroid)
        || (selectedNotificationType === NotificationType.Ios && !this.state.notificationDataIos)
        || (selectedNotificationType === NotificationType.Browser && !this.state.notificationDataBrowser));

    const doSubmit = async (): Promise<string | undefined> => {
      if (!!this.props.loggedInUser) {
        this.props.onLoggedInAndClose(this.props.loggedInUser.userId);
        return this.props.loggedInUser.userId;
      }
      this.setState({ isSubmitting: true });
      try {
        const userCreateResponse = await (await this.props.server.dispatch()).userCreate({
          projectId: this.props.server.getProjectId(),
          userCreate: {
            name: showDisplayNameInput ? this.state.displayName : undefined,
            email: showEmailInput ? this.state.email : undefined,
            password: (showPasswordInput && this.state.pass) ? saltHashPassword(this.state.pass) : undefined,
            iosPushToken: selectedNotificationType === NotificationType.Ios ? this.state.notificationDataIos : undefined,
            androidPushToken: selectedNotificationType === NotificationType.Android ? this.state.notificationDataAndroid : undefined,
            browserPushToken: selectedNotificationType === NotificationType.Browser ? this.state.notificationDataBrowser : undefined,
          },
        });
        if (userCreateResponse.requiresEmailLogin) {
          return new Promise(resolve => {
            this.setState({
              isSubmitting: false,
              emailLoginDialog: resolve,
            });
          })
        } else if (userCreateResponse.requiresEmailVerification) {
          return new Promise(resolve => {
            this.setState({
              isSubmitting: false,
              emailVerifyDialog: resolve,
            });
          })
        } else {
          this.setState({ isSubmitting: false });
          if (userCreateResponse.user) {
            this.props.onLoggedInAndClose(userCreateResponse.user.userId);
          }
          return userCreateResponse.user?.userId;
        }
      } catch (e) {
        this.setState({ isSubmitting: false });
        throw e;
      }
    };

    if (this.props.externalSubmit && this.externalSubmitEnabled !== isSubmittable) {
      this.externalSubmitEnabled = isSubmittable;
      this.props.externalSubmit(isSubmittable ? doSubmit : undefined);
    }

    const emailInput = !notifOpts.has(NotificationType.Email) ? undefined : (
      <TextField
        classes={{
          root: classNames(!!showEmailInputInline && this.props.classes.emailTextFieldInline),
        }}
        InputLabelProps={{
          classes: {
            root: classNames(!!showEmailInputInline && this.props.classes.emailInputLabelInline),
          },
        }}
        InputProps={{
          classes: {
            notchedOutline: classNames(!!showEmailInputInline && this.props.classes.emailInputInline),
          },
        }}
        inputRef={this.emailInputRef}
        variant='outlined'
        size='small'
        fullWidth
        required={!showEmailInputInline}
        value={this.state.email || ''}
        onChange={e => this.setState({ email: e.target.value })}
        label='Email'
        type='email'
        error={!!this.state.email && (!emailValid || !emailAllowedDomain)}
        helperText={(!!this.props.minimalistic || !!showEmailInputInline) ? undefined : (
          <span className={this.props.classes.noWrap}>
            {!this.state.email || emailAllowedDomain ? 'Where to send you updates' : `Allowed domains: ${onboarding?.notificationMethods.email?.allowedDomains?.join(', ')}`}
          </span>
        )}
        margin='normal'
        style={{ marginTop: showDisplayNameInput ? undefined : '0px' }}
        disabled={this.state.isSubmitting}
      />
    );

    const dialogContent = (
      <>
        <DialogContent className={classNames(
          this.props.className,
          this.props.inline && this.props.classes.contentInline,
        )}>
          {!!this.props.actionTitle && typeof this.props.actionTitle !== 'string' && this.props.actionTitle}
          <div>
            <div
              className={this.props.classes.content}
              style={singleColumnLayout ? { flexDirection: 'column' } : undefined}
            >
              <List component='nav' className={this.props.classes.notificationList}>
                {((!this.props.actionTitle && !this.props.minimalistic) || typeof this.props.actionTitle === 'string') && (
                  <ListSubheader className={this.props.classes.noWrap} component="div">{this.props.actionTitle !== undefined ? this.props.actionTitle : 'Create account'}</ListSubheader>
                )}
                <Collapse mountOnEnter in={notifOpts.has(NotificationType.SSO)}>
                  <ListItem
                    button={!onlySingleOption as any}
                    selected={!onlySingleOption && selectedNotificationType === NotificationType.SSO}
                    onClick={!onlySingleOption ? this.onClickSsoNotif.bind(this) : e => this.setState({ notificationType: NotificationType.SSO })}
                    disabled={this.state.isSubmitting}
                  >
                    <ListItemIcon>
                      {!onboarding?.notificationMethods.sso?.icon
                        ? (<NewWindowIcon />)
                        : (<DynamicMuiIcon name={onboarding?.notificationMethods.sso?.icon} />)}
                    </ListItemIcon>
                    <ListItemText primary={onboarding?.notificationMethods.sso?.buttonTitle
                      || this.props.config?.name
                      || 'External'} />
                  </ListItem>
                  <Collapse mountOnEnter in={onlySingleOption}>
                    <Button color='primary' className={this.props.classes.allowButton} onClick={this.onClickSsoNotif.bind(this)}>Open</Button>
                  </Collapse>
                </Collapse>
                {oauthOpts.map(oauthOpt => (
                  <Collapse mountOnEnter in={notifOpts.has(NotificationType.OAuth)}>
                    <ListItem
                      button={!onlySingleOption as any}
                      selected={!onlySingleOption && selectedNotificationType === NotificationType.OAuth && selectedOauthType === oauthOpt.oauthId}
                      onClick={!onlySingleOption
                        ? e => this.onClickOauthNotif(oauthOpt)
                        : e => this.setState({
                          notificationType: NotificationType.OAuth,
                          oauthType: oauthOpt.oauthId,
                        })}
                      disabled={this.state.isSubmitting}
                    >
                      <ListItemIcon>
                        {!oauthOpt.icon
                          ? (<NewWindowIcon />)
                          : (<DynamicMuiIcon name={oauthOpt.icon} />)}
                      </ListItemIcon>
                      <ListItemText primary={oauthOpt.buttonTitle} />
                    </ListItem>
                    <Collapse mountOnEnter in={onlySingleOption}>
                      <Button color='primary' className={this.props.classes.allowButton} onClick={e => this.onClickOauthNotif(oauthOpt)}>Open</Button>
                    </Collapse>
                  </Collapse>
                ))}
                <Collapse mountOnEnter in={notifOpts.has(NotificationType.Android) || notifOpts.has(NotificationType.Ios)}>
                  <ListItem
                    // https://github.com/mui-org/material-ui/pull/15049
                    button={!onlySingleOption as any}
                    selected={!onlySingleOption && (selectedNotificationType === NotificationType.Android || selectedNotificationType === NotificationType.Ios)}
                    onClick={!onlySingleOption ? this.onClickMobileNotif.bind(this) : undefined}
                    disabled={onlySingleOptionRequiresAllow || this.state.isSubmitting}
                  >
                    <ListItemIcon><MobilePushIcon /></ListItemIcon>
                    <ListItemText primary='Mobile Push' className={this.props.classes.noWrap} />
                  </ListItem>
                  <Collapse mountOnEnter in={onlySingleOptionRequiresAllow}>
                    <Button color='primary' className={this.props.classes.allowButton} onClick={this.onClickMobileNotif.bind(this)}>Allow</Button>
                  </Collapse>
                </Collapse>
                <Collapse mountOnEnter in={notifOpts.has(NotificationType.Browser)}>
                  <ListItem
                    button={!onlySingleOption as any}
                    selected={!onlySingleOption && selectedNotificationType === NotificationType.Browser}
                    onClick={!onlySingleOption ? this.onClickWebNotif.bind(this) : undefined}
                    disabled={onlySingleOptionRequiresAllow || this.state.isSubmitting}
                  >
                    <ListItemIcon><WebPushIcon /></ListItemIcon>
                    <ListItemText primary='Browser Push' className={this.props.classes.noWrap} />
                  </ListItem>
                  <Collapse mountOnEnter in={onlySingleOptionRequiresAllow}>
                    <Button color='primary' className={this.props.classes.allowButton} onClick={this.onClickWebNotif.bind(this)}>Allow</Button>
                  </Collapse>
                </Collapse>
                <Collapse mountOnEnter in={notifOpts.has(NotificationType.Email)}>
                  <ListItem
                    button={!onlySingleOption as any}
                    selected={!onlySingleOption && selectedNotificationType === NotificationType.Email}
                    onClick={!onlySingleOption ? e => {
                      this.setState({ notificationType: NotificationType.Email });
                      this.emailInputRef.current?.focus();
                    } : undefined}
                    disabled={this.state.isSubmitting}
                  >
                    <ListItemIcon><EmailIcon /></ListItemIcon>
                    <ListItemText className={this.props.classes.noWrap} primary={!showEmailInputInline ? 'Email' : emailInput} />
                  </ListItem>
                </Collapse>
                <Collapse mountOnEnter in={notifOpts.has(NotificationType.Silent)}>
                  <ListItem
                    button={!onlySingleOption as any}
                    selected={!onlySingleOption && selectedNotificationType === NotificationType.Silent}
                    onClick={!onlySingleOption ? e => this.setState({ notificationType: NotificationType.Silent }) : undefined}
                    disabled={this.state.isSubmitting}
                  >
                    <ListItemIcon><GuestIcon /></ListItemIcon>
                    <ListItemText primary={this.props.guestLabelOverride || 'Guest'} />
                  </ListItem>
                </Collapse>
                <Collapse mountOnEnter in={!signupAllowed}>
                  <ListItem
                    disabled={true}
                  >
                    <ListItemIcon><DisabledIcon /></ListItemIcon>
                    <ListItemText primary='Sign-up is not available' />
                  </ListItem>
                </Collapse>
              </List>
              <div
                className={this.props.classes.accountFieldsContainer}
                style={{
                  maxWidth: showAccountFields ? '400px' : '0px',
                  maxHeight: showAccountFields ? '400px' : '0px',
                }}
              >
                {!singleColumnLayout && (<Hr vertical isInsidePaper length='25%' />)}
                <div>
                  {!this.props.minimalistic && (
                    <ListSubheader className={this.props.classes.noWrap} component="div">Your info</ListSubheader>
                  )}
                  {showDisplayNameInput && (
                    <TextField
                      variant='outlined'
                      size='small'
                      fullWidth
                      required={isDisplayNameRequired}
                      value={this.state.displayName || ''}
                      onChange={e => this.setState({ displayName: e.target.value })}
                      label='Name'
                      helperText={!!this.props.minimalistic ? undefined : (<span className={this.props.classes.noWrap}>How others see you</span>)}
                      margin='normal'
                      classes={{ root: this.props.classes.noWrap }}
                      style={{ marginTop: '0px' }}
                      disabled={this.state.isSubmitting}
                    />
                  )}
                  <Collapse mountOnEnter in={showEmailInput} unmountOnExit>
                    <div>
                      {!showEmailInputInline && emailInput}
                      {showPasswordInput && (
                        <TextField
                          variant='outlined'
                          size='small'
                          fullWidth
                          required={isPasswordRequired}
                          value={this.state.pass || ''}
                          onChange={e => this.setState({ pass: e.target.value })}
                          label='Password'
                          helperText={!!this.props.minimalistic ? undefined : (
                            <span className={this.props.classes.noWrap}>
                              {isPasswordRequired
                                ? 'Secure your account'
                                : 'Optionally secure your account'}
                            </span>
                          )}
                          type={this.state.revealPassword ? 'text' : 'password'}
                          InputProps={{
                            endAdornment: (
                              <InputAdornment position='end'>
                                <IconButton
                                  aria-label='Toggle password visibility'
                                  onClick={() => this.setState({ revealPassword: !this.state.revealPassword })}
                                >
                                  {this.state.revealPassword ? <VisibilityIcon fontSize='small' /> : <VisibilityOffIcon fontSize='small' />}
                                </IconButton>
                              </InputAdornment>
                            )
                          }}
                          margin='normal'
                          disabled={this.state.isSubmitting}
                        />
                      )}
                    </div>
                  </Collapse>
                </div>
              </div>
            </div>
          </div>
          {signupAllowed && onboarding?.terms?.documents?.length && (
            <AcceptTerms overrideTerms={onboarding.terms.documents} />
          )}
          <Collapse mountOnEnter in={!!this.props.loggedInUser}>
            <DialogContentText>You are logged in as <span className={this.props.classes.bold}>{this.props.loggedInUser?.name || this.props.loggedInUser?.email || 'Anonymous'}</span></DialogContentText>
          </Collapse>
        </DialogContent>
        {!this.props.externalSubmit && (
          <DialogActions>
            {!!this.props.loggedInUser && !!this.props.onClose && (
              <Button onClick={this.props.onClose.bind(this)}>Cancel</Button>
            )}
            {!!signupAllowed ? (
              <SubmitButton
                color='primary'
                isSubmitting={this.state.isSubmitting}
                disabled={!isSubmittable && !this.props.loggedInUser}
                onClick={doSubmit}
              >{this.props.actionSubmitTitle || 'Continue'}</SubmitButton>
            ) : (!!this.props.onClose ? (
              <Button onClick={() => { this.props.onClose?.() }}>Back</Button>
            ) : null)}
          </DialogActions>
        )}
        <Dialog
          open={!!this.state.awaitExternalBind}
          onClose={() => this.setState({ awaitExternalBind: undefined })}
          maxWidth='xs'
          {...this.props.forgotEmailDialogProps}
        >
          <DialogTitle>Awaiting confirmation...</DialogTitle>
          <DialogContent>
            {this.state.awaitExternalBind === 'recovery' ? (
              <DialogContentText>We sent an email to <span className={this.props.classes.bold}>{this.state.email}</span>. Return to this page after clicking the confirmation link.</DialogContentText>
            ) : (
              <DialogContentText>A popup was opened leading you to a sign-in page. After you complete sign-in, this dialog will automatically close.</DialogContentText>
            )}
          </DialogContent>
          <DialogActions>
            <Button onClick={() => this.setState({ awaitExternalBind: undefined })}>Cancel</Button>
          </DialogActions>
        </Dialog>
        <Dialog
          open={!!this.state.emailLoginDialog}
          onClose={() => {
            this.state.emailLoginDialog?.();
            this.setState({ emailLoginDialog: undefined })
          }}
          maxWidth='xs'
        >
          <DialogTitle>Login via Email</DialogTitle>
          <DialogContent>
            <DialogContentText>The email <span className={this.props.classes.bold}>{this.state.email}</span> is associated with an account.</DialogContentText>
            <DialogContentText>Open the link from the email or copy the verification token here:</DialogContentText>
            <DigitsInput
              digits={6}
              value={this.state.emailLoginToken}
              disabled={this.state.isSubmitting}
              onChange={(val, isComplete) => {
                if (isComplete) {
                  this.setState({
                    emailLoginToken: val,
                    isSubmitting: true,
                  }, () => setTimeout(() => {
                    this.props.server.dispatch().then(d => d.userLogin({
                      projectId: this.props.server.getProjectId(),
                      userLogin: {
                        email: this.state.email!,
                        token: val.join(''),
                      },
                    })).then(user => {
                      this.state.emailLoginDialog?.(user.userId);
                      this.setState({
                        isSubmitting: false,
                        emailLoginDialog: undefined,
                      });
                      this.props.onLoggedInAndClose(user.userId);
                    }).catch(() => {
                      this.setState({ isSubmitting: false });
                    });
                  }, 1));
                } else {
                  this.setState({ emailLoginToken: val });
                }
              }}
            />
          </DialogContent>
          <DialogActions>
            <Button onClick={() => {
              this.state.emailLoginDialog?.();
              this.setState({ emailLoginDialog: undefined })
            }}>Cancel</Button>
          </DialogActions>
        </Dialog>
        <Dialog
          open={!!this.state.emailVerifyDialog}
          onClose={() => {
            this.state.emailVerifyDialog?.();
            this.setState({ emailVerifyDialog: undefined });
          }}
          maxWidth='xs'
        >
          <DialogTitle>Verify your email</DialogTitle>
          <DialogContent>
            <DialogContentText>We sent a verification email to <span className={this.props.classes.bold}>{this.state.email}</span>. Please copy the verification token from the email here:</DialogContentText>
            <DigitsInput
              digits={6}
              value={this.state.emailVerification}
              disabled={this.state.isSubmitting}
              onChange={(val, isComplete) => {
                if (isComplete) {
                  this.setState({
                    emailVerification: val,
                    isSubmitting: true,
                  }, () => setTimeout(() => {
                    this.props.server.dispatch().then(d => d.userCreate({
                      projectId: this.props.server.getProjectId(),
                      userCreate: {
                        name: this.state.displayName,
                        email: this.state.email!,
                        emailVerification: val.join(''),
                        password: this.state.pass ? saltHashPassword(this.state.pass) : undefined,
                      },
                    })).then(userCreateResponse => {
                      if (userCreateResponse.requiresEmailVerification || !userCreateResponse.user) {
                        this.setState({ isSubmitting: false });
                      } else {
                        this.state.emailVerifyDialog?.(userCreateResponse.user.userId);
                        this.setState({
                          isSubmitting: false,
                          emailVerifyDialog: undefined,
                        });
                        this.props.onLoggedInAndClose(userCreateResponse.user.userId);
                      }
                    }).catch(() => {
                      this.setState({ isSubmitting: false });
                    });
                  }, 1));
                } else {
                  this.setState({ emailVerification: val });
                }
              }}
            />
          </DialogContent>
          <DialogActions>
            <Button onClick={() => {
              this.state.emailVerifyDialog?.();
              this.setState({ emailVerifyDialog: undefined })
            }}>Cancel</Button>
          </DialogActions>
        </Dialog>
      </>
    );

    return this.props.inline ? (
      <Collapse mountOnEnter in={!!this.props.open}>
        {dialogContent}
      </Collapse>
    ) : (
      <Dialog
        open={!!this.props.open}
        onClose={this.props.onClose}
        scroll='body'
        PaperProps={{
          style: {
            width: 'fit-content',
            marginLeft: 'auto',
            marginRight: 'auto',
          },
        }}
        {...this.props.DialogProps}
      >
        {dialogContent}
      </Dialog>
    );
  }
Example #18
Source File: Settings.tsx    From back-home-safe with GNU General Public License v3.0 4 votes vote down vote up
Settings = () => {
  const { t } = useTranslation("main_screen");
  const { hasCameraSupport } = useCamera();
  const { autoRemoveRecordDay, setAutoRemoveRecordDay } = useTravelRecord();
  const { incognito, setIncognito, value } = useData();
  const [languageOpen, setLanguageOpen] = useState(false);
  const { language, setLanguage } = useI18n();

  const handleLanguageClick = () => {
    setLanguageOpen(!languageOpen);
  };

  const handleExportData = () => {
    const dataStr =
      "data:text/json;charset=utf-8," +
      encodeURIComponent(JSON.stringify(value));
    const downloadAnchorNode = document.createElement("a");
    downloadAnchorNode.setAttribute("href", dataStr);
    downloadAnchorNode.setAttribute("download", "export.json");
    document.body.appendChild(downloadAnchorNode); // required for firefox
    downloadAnchorNode.click();
    downloadAnchorNode.remove();
  };

  return (
    <PageWrapper>
      <Header name={t("setting.name")} />
      <ContentWrapper>
        <StyledList
          subheader={
            <ListSubheader>{t("setting.section.common")}</ListSubheader>
          }
        >
          {hasCameraSupport ? (
            <StyledLink to="/cameraSetting">
              <ListItem button>
                <ListItemText primary={t("setting.item.camera_setting")} />
              </ListItem>
            </StyledLink>
          ) : (
            <ListItem button disabled>
              <ListItemText primary={t("setting.item.camera_setting")} />
            </ListItem>
          )}
          <StyledLink to="/confirmPageSetting">
            <ListItem button>
              <ListItemText primary={t("setting.item.confirm_page_setting")} />
            </ListItem>
          </StyledLink>
          <ListItem>
            <ListItemText primary={t("setting.item.auto_delete_record")} />
            <ListItemSecondaryAction>
              <Select
                labelId="cameraId"
                id="demo-simple-select"
                value={autoRemoveRecordDay}
                onChange={(e) => {
                  setAutoRemoveRecordDay(e.target.value as number);
                }}
              >
                {range(1, 100).map((day) => (
                  <MenuItem value={day} key={day}>
                    {day}{" "}
                    {day === 1 ? t("setting.form.day") : t("setting.form.days")}
                  </MenuItem>
                ))}
              </Select>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem>
            <ListItemText
              primary={t("setting.item.incognito_mode.name")}
              secondary={t("setting.item.incognito_mode.explanation")}
            />
            <ListItemSecondaryAction>
              <Switch
                checked={incognito}
                onChange={(e) => {
                  setIncognito(e.target.checked);
                }}
                color="primary"
              />
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem button onClick={handleLanguageClick}>
            <ListItemText primary={t("setting.item.language")} />
            {languageOpen ? <ExpandLess /> : <ExpandMore />}
          </ListItem>
          <Collapse in={languageOpen} timeout="auto" unmountOnExit>
            <ListItem>
              <RadioGroup
                aria-label="language"
                name="language"
                value={language}
                onChange={(event) => {
                  setLanguage(event.target.value as languageType);
                }}
              >
                <FormControlLabel
                  value={languageType["ZH-HK"]}
                  control={<Radio />}
                  label="繁體中文"
                />
                <FormControlLabel
                  value={languageType.EN}
                  control={<Radio />}
                  label="English"
                />
              </RadioGroup>
            </ListItem>
          </Collapse>
        </StyledList>
        <Divider />
        <StyledList
          subheader={<ListSubheader>{t("setting.section.lab")}</ListSubheader>}
        >
          <StyledLink to="/qrGenerator">
            <ListItem button>
              <ListItemText primary={t("setting.item.qr_generator")} />
            </ListItem>
          </StyledLink>
          <StyledLink to="/vaccinationQRReader">
            <ListItem button>
              <ListItemText primary={t("setting.item.vaccinationQRReader")} />
            </ListItem>
          </StyledLink>
          <ListItem onClick={handleExportData}>
            <ListItemText primary={t("setting.item.export_data")} />
          </ListItem>
          <ListItem button>
            <ListItemText
              primary={t("setting.item.reset")}
              onClick={clearAllData}
            />
          </ListItem>
        </StyledList>
        <Divider />
        <StyledList
          subheader={
            <ListSubheader>
              {t("setting.section.version")}: {__APP_VERSION__}
            </ListSubheader>
          }
        >
          <StyledExternalLink
            href="https://gitlab.com/codogo-b/back-home-safe"
            target="_blank"
          >
            <ListItem button>
              <ListItemText primary={t("setting.item.about_us")} />
            </ListItem>
          </StyledExternalLink>
          <StyledLink to="/disclaimer">
            <ListItem button>
              <ListItemText primary={t("setting.item.disclaimer")} />
            </ListItem>
          </StyledLink>
          <StyledExternalLink
            href="https://gitlab.com/codogo-b/back-home-safe/-/blob/master/CHANGELOG.md"
            target="_blank"
          >
            <ListItem button>
              <ListItemText primary={t("setting.item.change_log")} />
            </ListItem>
          </StyledExternalLink>
          <StyledExternalLink
            href="https://gitlab.com/codogo-b/back-home-safe/-/issues"
            target="_blank"
          >
            <ListItem button>
              <ListItemText primary={t("setting.item.report_issue")} />
            </ListItem>
          </StyledExternalLink>
        </StyledList>
      </ContentWrapper>
    </PageWrapper>
  );
}
Example #19
Source File: AccountManagementPage.tsx    From clarity with Apache License 2.0 4 votes vote down vote up
AccountManagementPage = observer((props: Props) => {
  const [openDialog, setOpenDialog] = React.useState(false);
  const [openKeyDialog, setOpenKeyDialog] = React.useState(false);
  const [
    selectedAccount,
    setSelectedAccount
  ] = React.useState<SignKeyPairWithAlias | null>(null);
  const [name, setName] = React.useState('');
  const [publicKey64, setPublicKey64] = React.useState('');
  const [publicKeyHex, setPublicKeyHex] = React.useState('');
  /* Note: 01 prefix denotes algorithm used in key generation */
  const address = '01' + publicKeyHex;
  const [copyStatus, setCopyStatus] = React.useState(false);

  const handleClickOpen = (account: SignKeyPairWithAlias) => {
    setOpenDialog(true);
    setSelectedAccount(account);
    setName(account.name);
  };

  const handleViewKey = async (accountName: string) => {
    let publicKey64 = await props.authContainer.getSelectedAccountKey(
      accountName
    );
    let publicKeyHex = await props.authContainer.getPublicKeyHex(accountName);
    setName(accountName);
    setPublicKey64(publicKey64);
    setPublicKeyHex(publicKeyHex);
    setOpenKeyDialog(true);
  };

  const handleCopyMessage = (event?: React.SyntheticEvent, reason?: string) => {
    if (reason === 'clickaway') {
      return;
    }
    setCopyStatus(false);
  };

  const handleClose = () => {
    setOpenDialog(false);
    setOpenKeyDialog(false);
    setSelectedAccount(null);
  };

  const handleUpdateName = () => {
    if (selectedAccount) {
      props.authContainer.renameUserAccount(selectedAccount.name, name);
      handleClose();
    }
  };

  const onDragEnd = (result: DropResult) => {
    // dropped outside the list
    if (!result.destination) {
      return;
    }

    props.authContainer.reorderAccount(
      result.source.index,
      result.destination.index
    );
  };

  const handleClickRemove = (name: string) => {
    confirm(
      <div className="text-danger">Remove account</div>,
      'Are you sure you want to remove this account?'
    ).then(() => props.authContainer.removeUserAccount(name));
  };

  return (
    <React.Fragment>
      <DragDropContext onDragEnd={result => onDragEnd(result)}>
        <Droppable droppableId="droppable">
          {(provided, snapshot) => (
            <Observer>
              {() => (
                <RootRef rootRef={provided.innerRef}>
                  <List>
                    {props.authContainer.userAccounts.map((item, index) => (
                      <Draggable
                        key={item.name}
                        draggableId={item.name}
                        index={index}
                      >
                        {(provided, snapshot) => (
                          <ListItem
                            innerRef={provided.innerRef}
                            ContainerProps={{
                              ...provided.draggableProps,
                              ...provided.dragHandleProps,
                              style: getItemStyle(
                                snapshot.isDragging,
                                provided.draggableProps.style
                              )
                            }}
                          >
                            <ListItemText primary={item.name} />
                            <ListItemSecondaryAction>
                              <IconButton
                                edge={'end'}
                                onClick={() => {
                                  handleClickOpen(item);
                                }}
                              >
                                <EditIcon />
                              </IconButton>
                              <IconButton
                                edge={'end'}
                                onClick={() => {
                                  handleClickRemove(item.name);
                                }}
                              >
                                <DeleteIcon />
                              </IconButton>
                              <IconButton
                                edge={'end'}
                                onClick={() => {
                                  handleViewKey(item.name);
                                }}
                              >
                                <VpnKeyIcon />
                              </IconButton>
                            </ListItemSecondaryAction>
                          </ListItem>
                        )}
                      </Draggable>
                    ))}
                    {provided.placeholder}
                  </List>
                </RootRef>
              )}
            </Observer>
          )}
        </Droppable>
      </DragDropContext>
      <Dialog
        open={openDialog}
        onClose={handleClose}
        aria-labelledby="form-dialog-title"
      >
        <DialogTitle id="form-dialog-title">Rename</DialogTitle>
        <DialogContent>
          <Input
            autoFocus
            margin="dense"
            id="name"
            type="text"
            fullWidth
            value={name}
            onChange={e => {
              setName(e.target.value);
            }}
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose} color="primary">
            Cancel
          </Button>
          <Button onClick={handleUpdateName} color="primary">
            Update
          </Button>
        </DialogActions>
      </Dialog>

      <Dialog
        fullScreen
        open={openKeyDialog}
        onClose={handleClose}
        aria-labelledby="form-dialog-title"
      >
        <DialogTitle id="form-dialog-title">Account Details</DialogTitle>
        <DialogContent>
          <List>
            <ListSubheader>
              <Typography variant={'h6'}>{name}</Typography>
            </ListSubheader>
            <ListItem>
              <IconButton
                edge={'start'}
                onClick={() => {
                  copy(address);
                  setCopyStatus(true);
                }}
              >
                <FilterNoneIcon />
              </IconButton>
              <ListItemText
                primary={'Address: ' + address}
                style={{ overflowWrap: 'break-word' }}
              />
            </ListItem>
            <ListItem>
              <IconButton
                edge={'start'}
                onClick={() => {
                  copy(publicKey64);
                  setCopyStatus(true);
                }}
              >
                <FilterNoneIcon />
              </IconButton>
              <ListItemText
                primary={'Public Key: ' + publicKey64}
                style={{ overflowWrap: 'break-word' }}
              />
            </ListItem>
          </List>
          <Snackbar
            open={copyStatus}
            message="Copied!"
            autoHideDuration={1500}
            onClose={handleCopyMessage}
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose} color="primary">
            Close
          </Button>
        </DialogActions>
      </Dialog>
    </React.Fragment>
  );
})
Example #20
Source File: SettingsPage.tsx    From shadowsocks-electron with GNU General Public License v3.0 4 votes vote down vote up
SettingsPage: React.FC = () => {
  const styles = useStyles();
  const { t } = useTranslation();

  const dispatch = useTypedDispatch();
  const [form] = Form.useForm();
  const settings = useTypedSelector(state => state.settings);
  const config = useTypedSelector(state => state.config);
  // const [aclVisible, setAclVisible] = useState(false);
  const inputFileRef = React.useRef<HTMLInputElement>(null);
  const [DialogConfirm, showDialog, closeDialog] = useDialogConfirm();
  const settingKeys = useRef(
    ['localPort', 'pacPort', 'gfwListUrl',
    'httpProxy', 'autoLaunch', 'fixedMenu',
    'darkMode', 'autoTheme', 'verbose', 'autoHide']
  );
  const cachedRef = useRef<any>(null);

  const enqueueSnackbar = (message: SnackbarMessage, options: Notification) => {
    dispatch(enqueueSnackbarAction(message, options))
  };

  useEffect(() => {
    dispatch<any>(getStartupOnBoot());
  }, [dispatch]);

  /* dark mode */
  useEffect(() => {
    if (
      (persistStore.get('darkMode') === 'true' && !settings.darkMode) ||
      (persistStore.get('darkMode') === 'false' && !!settings.darkMode) ||
      (persistStore.get('darkMode') === undefined && !!settings.darkMode)
    ) {
      dispatchEvent({
        type: 'theme:update',
        payload: {
          shouldUseDarkColors: !!settings.darkMode
        }
      });
    }
  }, [settings.darkMode]);

  /* restoreFromFile */
  useMemo(() => {
    const obj = {};
    if (cachedRef.current) {
      settingKeys.current.forEach(key => {
        if (cachedRef.current[key] !== (settings as any)[key]) {
          if (key === 'httpProxy') {
            Object.assign(obj, {
              httpProxy: settings.httpProxy.enable,
              httpProxyPort: settings.httpProxy.port,
            });
          } else {
            Object.assign(obj, { [key]: (settings as any)[key] });
          }
        }
      });
      form.setFieldsValue(obj);
    }
    cachedRef.current = settingKeys.current.reduce(
      (pre, cur) => Object.assign(pre, { [cur]: (settings as any)[cur] }),
      {}
    );
  }, settingKeys.current.map(key => (settings as any)[key]));

  const backupConfiguration = () => {
    return backupConfigurationToFile({
      config,
      settings
    });
  };

  const restoreConfiguration = () => {
    dispatch<any>(restoreConfigurationFromFile({
      success: t('the_recovery_is_successful'),
      error: {
        default: t('the_recovery_is_failed'),
        404: t('user_canceled')
      }
    }));
  }

  const checkPortValid = (parsedValue: number) => {
    if (!(parsedValue && parsedValue > 1024 && parsedValue <= 65535)) {
          return Promise.reject(t("invalid_port_range"));
    }
    return Promise.resolve();
  };

  const checkPortSame = () => {
    const localPort = +form.getFieldValue('localPort');
    const pacPort = +form.getFieldValue('pacPort');
    const httpPort = +form.getFieldValue('httpProxyPort');
    const num = localPort ^ pacPort ^ httpPort;
    if (num === localPort || num === pacPort || num === httpPort) {
      return Promise.reject(t("the_same_port_is_not_allowed"));
    }
    return Promise.resolve();
  };

  const handleOpenLog = async () => {
    await MessageChannel.invoke('main', 'service:desktop', {
      action: 'openLogDir',
      params: {}
    });
  };

  const handleOpenProcessManager = async () => {
    await MessageChannel.invoke('main', 'service:desktop', {
      action: 'openProcessManager',
      params: {}
    });
  };

  const handleReset = () => {
    dispatch({
      type: CLEAR_STORE
    } as any);
    closeDialog();
    MessageChannel.invoke('main', 'service:main', {
      action: 'stopClient',
      params: {}
    });
    enqueueSnackbar(t('cleared_all_data'), { variant: 'success' });
  };

  const handleAlertDialogOpen = () => {
    showDialog(t('reset_all_data'), t('reset_all_data_tips'));
  };

  const handleAlertDialogClose = () => {
    closeDialog();
  };

  const reGeneratePacFileWithFile = () => {
    inputFileRef.current?.click();
  }

  const reGeneratePacFileWithUrl = () => {
    reGeneratePacFile({
      url: settings.gfwListUrl
    });
  }

  const onGFWListFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) {
      const reader = new FileReader();
      reader.onload = (e: any) => {
        const text = e.target.result;
        if (text) {
          reGeneratePacFile({
            text: text
          });
        }
      };
      reader.readAsText(file);
    }
  }

  const reGeneratePacFile = (params: { url?: string, text?: string }) => {
    dispatch<any>(setStatus('waiting', true));
    MessageChannel.invoke('main', 'service:main', {
      action: 'reGeneratePacFile',
      params
    }).then((rsp) => {
      setTimeout(() => { dispatch<any>(setStatus('waiting', false)); }, 1e3);
      if (rsp.code === 200) {
        enqueueSnackbar(t('successful_operation'), { variant: 'success' });
      } else {
        enqueueSnackbar(t('failed_to_download_file'), { variant: 'error' });
      }
    });
  }

  const onLangChange = (e: React.ChangeEvent<{ name?: string | undefined, value: unknown; }>) => {
    if (persistStore.get('lang') === e.target.value) return;
    persistStore.set('lang', e.target.value as string);
    MessageChannel.invoke('main', 'service:desktop', {
      action: 'reloadMainWindow',
      params: {}
    });
    MessageChannel.invoke('main', 'service:desktop', {
      action: 'setLocale',
      params: getFirstLanguage(e.target.value as string)
    });
  }

  const onAutoThemeChange = (e: React.ChangeEvent<{ name?: string | undefined, checked: boolean; }>) => {
    const checked = e.target.checked;
    MessageChannel.invoke('main', 'service:theme', {
      action: checked ? 'listenForUpdate' : 'unlistenForUpdate',
      params: {}
    }).then(rsp => {
      if (rsp.code === 200) {
        persistStore.set('autoTheme', checked ? 'true' : 'false');
      }
    });
    MessageChannel.invoke('main', 'service:theme', {
      action: 'getSystemThemeInfo',
      params: {}
    })
    .then(rsp => {
      if (rsp.code === 200) {
        dispatchEvent({
          type: 'theme:update',
          payload: rsp.result
        });
        if (!checked) {
          form.setFieldsValue({
            darkMode: rsp.result?.shouldUseDarkColors
          });
        }
      }
    });

  }

  const checkPortField = (rule: any, value: any) => {
    return Promise.all([checkPortSame(), checkPortValid(value)]);
  };

  const onFieldChange = (changedFields: { [key: string]: any }, allFields: { [key: string]: any }) => {
    const keys = Object.keys(changedFields);
    keys.forEach((key) => {
      let value = changedFields[key];
      form.validateFields([key]).then(() => {
        switch (key) {
          case 'httpProxy':
            value = {
              ...settings.httpProxy,
              enable: value
            };
            dispatch(setSetting<'httpProxy'>(key, value))
            setHttpAndHttpsProxy({ ...value, type: 'http', proxyPort: settings.localPort });
            return;
          case 'httpProxyPort':
            value = {
              ...settings.httpProxy,
              port: value
            };
            dispatch(setSetting<'httpProxy'>('httpProxy', value))
            setHttpAndHttpsProxy({ ...value, type: 'http', proxyPort: settings.localPort });
            return;
          case 'acl':
            dispatch(setSetting<'acl'>(key, {
              ...settings.acl,
              text: value
            }));
            return;
          case 'autoLaunch':
            dispatch<any>(setStartupOnBoot(value));
            return;
          case 'darkMode':
            dispatchEvent({
              type: 'theme:update',
              payload: {
                shouldUseDarkColors: value
              }
            });
            break;
          default:
            break;
        }

        dispatch(setSetting<any>(key, value));
      }).catch((reason: { errorFields: { errors: string[] }[] }) => {
        enqueueSnackbar(reason?.errorFields?.map(item => item.errors.join()).join(), { variant: 'error' });
      });
    });
  }

  return (
    <Container className={styles.container}>
      <Form
        form={form}
        initialValues={
          {
            localPort: settings.localPort,
            pacPort: settings.pacPort,
            gfwListUrl: settings.gfwListUrl,
            httpProxy: settings.httpProxy.enable,
            httpProxyPort: settings.httpProxy.port,
            autoLaunch: settings.autoLaunch,
            fixedMenu: settings.fixedMenu,
            darkMode: settings.darkMode,
            autoTheme: settings.autoTheme,
            verbose: settings.verbose,
            autoHide: settings.autoHide,
          }
        }
        onValuesChange={onFieldChange}
      >
        <Field
          name="localPort"
          rules={[
            { required: true, message: t('invalid_value') },
            { validator: checkPortField },
          ]}
          normalize={(value: string) => +(value.trim())}
          validateTrigger={false}
        >
          <TextField
            className={styles.textField}
            required
            fullWidth
            size="small"
            type="number"
            label={t('local_port')}
            placeholder={t('local_port_tips')}
          />
        </Field>
        <Field
          name="pacPort"
          rules={[
            { required: true, message: t('invalid_value') },
            { validator: checkPortField }
          ]}
          normalize={(value: string) => +(value.trim())}
          validateTrigger={false}
        >
          <TextField
            className={styles.textField}
            required
            fullWidth
            type="number"
            size="small"
            label={t('pac_port')}
            placeholder={t('pac_port_tips')}
          />
        </Field>
        <Field
          name="gfwListUrl"
          validateTrigger={false}
        >
          <TextField
          className={styles.textField}
          required
          fullWidth
          type="url"
          size="small"
          label={
            <TextWithTooltip
              text={t('gfwlist_url')}
              icon={
                <span>
                  <Tooltip arrow placement="top" title={t('recover_pac_file_with_link') as string}>
                    <RestorePage className={styles.cursorPointer} onClick={reGeneratePacFileWithUrl} />
                  </Tooltip>
                  <Tooltip arrow placement="top" title={t('recover_pac_file_with_file') as string}>
                    <NoteAdd className={styles.cursorPointer} onClick={reGeneratePacFileWithFile}/>
                  </Tooltip>
                </span>
              }
            />
          }
          placeholder={t('gfwlist_url_tips')}
        />
        </Field>
        <input onChange={onGFWListFileChange} ref={inputFileRef} type={'file'} multiple={false} style={{ display: 'none' }}></input>
        <List className={styles.list}>
          <ListItem>
              <ListItemText
                primary={t('http_proxy')}
              />
              <ListItemSecondaryAction>
                <Field name="httpProxy" valuePropName="checked">
                  <AdaptiveSwitch
                    edge="end"
                  />
                </Field>
              </ListItemSecondaryAction>
          </ListItem>
          {
            settings.httpProxy.enable && (
              <ListItem>
                <ListItemText
                  primary={t('http_proxy_port')}
                  secondary={t('restart_when_changed')}
                />
                <ListItemSecondaryAction>
                  <Field
                    name="httpProxyPort"
                    rules={[
                      { required: true, message: t('invalid_value') },
                      { validator: checkPortField }
                    ]}
                    normalize={(value: string) => +(value.trim())}
                    validateTrigger={false}
                  >
                    <TextField
                      className={`${styles.textField} ${styles.indentInput}`}
                      required
                      size="small"
                      type="number"
                      placeholder={t('http_proxy_port')}
                    />
                  </Field>
                </ListItemSecondaryAction>
              </ListItem>
            )
          }
          {/* <ListItem>
              <ListItemText
                primary={'ACL'}
                // secondary="Not applicable to Linux"
              />
              <ListItemSecondaryAction>
                <AdaptiveSwitch
                  edge="end"
                  checked={settings.acl.enable}
                  onChange={e => handleSwitchValueChange("acl", e)}
                />
              </ListItemSecondaryAction>
          </ListItem>
          {
            settings.acl.enable && (
              <ListItem>
                <ListItemText
                  primary={t('acl_content')}
                />
                <ListItemSecondaryAction>
                  <TextField
                    className={`${styles.textField} ${styles.indentInput}`}
                    style={{ width: '120px', textAlign: 'right' }}
                    required
                    size="small"
                    type="text"
                    placeholder={t('click_to_edit')}
                    onClick={() => setAclVisible(true)}
                    value={'*****'}
                  />
                </ListItemSecondaryAction>
              </ListItem>
            )
          } */}
          <ListItem>
            <ListItemText
              primary={t('launch_on_boot')}
              secondary={t('not_applicable_to_linux_snap_application')}
            />
            <ListItemSecondaryAction>
              <Field name="autoLaunch" valuePropName="checked">
                <AdaptiveSwitch
                  edge="end"
                />
              </Field>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem>
            <ListItemText
              primary={t('fixed_menu')}
            />
            <ListItemSecondaryAction>
              <Field name="fixedMenu" valuePropName="checked">
                <AdaptiveSwitch
                  edge="end"
                />
              </Field>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem>
            <ListItemText
              primary={t('auto_hide')}
              secondary={t('minimize_on_start')}
            />
            <ListItemSecondaryAction>
              <Field name="autoHide" valuePropName="checked">
                <AdaptiveSwitch
                  edge="end"
                />
              </Field>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem>
            <ListItemText
              primary={t('autoTheme')}
              secondary={t('autoThemeTips')}
            />
            <ListItemSecondaryAction>
              <Field name="autoTheme" valuePropName="checked">
                <AdaptiveSwitch
                  edge="end"
                  onChange={onAutoThemeChange}
                />
              </Field>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem>
            <ListItemText
              primary={t('darkMode')}
            />
            <ListItemSecondaryAction>
              <Field name="darkMode" valuePropName="checked">
                <AdaptiveSwitch
                  edge="end"
                  disabled={settings.autoTheme}
                />
              </Field>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem>
            <ListItemText
              primary={'Language'}
            />
            <ListItemSecondaryAction>
              <Select
                value={getDefaultLang()}
                onChange={onLangChange}
              >
              <MenuItem value={'en-US'}>English</MenuItem>
              <MenuItem value={'zh-CN'}>中文简体</MenuItem>
            </Select>
            </ListItemSecondaryAction>
          </ListItem>

          <ListItem button onClick={backupConfiguration}>
              <ListItemText primary={t('backup')} />
          </ListItem>
          <ListItem button onClick={() => restoreConfiguration()}>
              <ListItemText primary={t('restore')} />
          </ListItem>
          <ListItem button onClick={handleAlertDialogOpen}>
            <ListItemText primary={t('reset_data')} />
          </ListItem>

          <Divider className={styles.margin} />

          <ListSubheader>{t('debugging')}</ListSubheader>

          <ListItem>
            <ListItemText
              primary="Verbose"
              secondary={t('verbose_output')}
            />
            <ListItemSecondaryAction>
              <Field name="verbose" valuePropName="checked">
                <AdaptiveSwitch
                  edge="end"
                />
              </Field>
            </ListItemSecondaryAction>
          </ListItem>
          <ListItem button onClick={handleOpenLog}>
            <ListItemText primary={t('open_log_dir')} />
          </ListItem>
          <ListItem button onClick={handleOpenProcessManager}>
            <ListItemText primary={t('open_process_manager')} />
          </ListItem>
        </List>

      </Form>

      {/* dialog */}

      {/* <EditAclDialog
        open={aclVisible}
        onClose={() => setAclVisible(false)}
        children={undefined}
        onTextChange={handleValueChange}
      /> */}

      <DialogConfirm onClose={handleAlertDialogClose} onConfirm={handleReset} />
    </Container>
  );
}
Example #21
Source File: ResponsiveDrawer.tsx    From UsTaxes with GNU Affero General Public License v3.0 4 votes vote down vote up
function ResponsiveDrawer(props: DrawerItemsProps): ReactElement {
  const classes = useStyles({ isMobile })
  const theme = useTheme()

  const { sections, isOpen, setOpen } = props

  const drawer = (
    <>
      {/* {isMobile && <Toolbar />} */}
      {sections.map(({ title, items }) => (
        <Fragment key={`section ${title}`}>
          <List
            subheader={<ListSubheader disableSticky>{title}</ListSubheader>}
            className={classes.list}
          >
            {items.map((item) => (
              <ListItem
                button
                classes={{}}
                key={item.title}
                component={NavLink}
                selected={location.pathname === item.url}
                to={item.url}
              >
                <ListItemText primary={`${item.title}`} />
              </ListItem>
            ))}
          </List>
          <Divider />
        </Fragment>
      ))}
      <List className={classes.listSocial}>
        <ListItem className={classes.listItemSocial}>
          <Link to={Urls.help}>
            <IconButton color="secondary" aria-label="help, support, feedback">
              <HelpOutlineRounded />
            </IconButton>
          </Link>
          <IconButton
            color="secondary"
            aria-label="github, opens in new tab"
            component="a"
            href={`https://github.com/ustaxes/UsTaxes`}
            target="_blank"
            rel="noreferrer noopener"
          >
            <GitHubIcon />
          </IconButton>
        </ListItem>
        <ListItem className={classes.listItemSocial}>
          <IconButton
            color="secondary"
            aria-label="twitter, opens in new tab"
            component="a"
            href={`https://www.twitter.com/ustaxesorg`}
            target="_blank"
            rel="noreferrer noopener"
          >
            <TwitterIcon />
          </IconButton>
        </ListItem>
        <ListItem className={classes.listItemSocial}>
          <Link to={Urls.settings}>
            <IconButton color="secondary" aria-label="site user settings">
              <Settings />
            </IconButton>
          </Link>
        </ListItem>
      </List>
    </>
  )

  return (
    <nav className={classes.drawer} aria-label="primary">
      <SwipeableDrawer
        variant={!isMobile ? 'persistent' : undefined}
        anchor={theme.direction === 'rtl' ? 'right' : 'left'}
        open={isOpen}
        onOpen={() => setOpen(true)}
        onClose={() => setOpen(false)}
        classes={{
          root: classes.drawerContainer,
          paper: classes.drawerPaper
        }}
        ModalProps={{
          BackdropProps: {
            classes: { root: classes.drawerBackdrop }
          }
          // Disabling for the time being due to scroll position persisting
          // keepMounted: isMobile ? true : false // Better open performance on mobile.
        }}
      >
        {drawer}
      </SwipeableDrawer>
    </nav>
  )
}