@material-ui/core#Snackbar TypeScript Examples

The following examples show how to use @material-ui/core#Snackbar. 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: Snackbar.tsx    From Demae with MIT License 6 votes vote down vote up
Bar = ({ open, severity, vertical, message, onClose }: { open: boolean, severity: Severity, vertical?: "top" | "bottom", message?: string, onClose: (event: React.SyntheticEvent | React.MouseEvent, reason?: string) => void }) => {
	if (open) {
		return (
			<Snackbar
				anchorOrigin={{
					vertical: vertical || "top",
					horizontal: "center",
				}}
				open={open}
				autoHideDuration={2800}
				onClose={onClose}
				message={
					<Box display="flex" alignItems="center" fontSize={14} fontWeight={400}>
						{severity === "error" && <ErrorIcon fontSize="small" style={{ marginRight: "8px", color: ErrorColor }} />}
						{severity === "warning" && <WarningIcon fontSize="small" style={{ marginRight: "8px", color: WarningColor }} />}
						{severity === "info" && <InfoIcon fontSize="small" style={{ marginRight: "8px", color: InfoColor }} />}
						{severity === "success" && <CheckCircleIcon fontSize="small" style={{ marginRight: "8px", color: SuccessColor }} />}
						{message}
					</Box>
				}
				action={
					<React.Fragment>
						<IconButton size="small" aria-label="close" color="inherit" onClick={onClose}>
							<CloseIcon fontSize="small" />
						</IconButton>
					</React.Fragment>
				}
			/>
		)
	}
	return <></>
}
Example #2
Source File: snack.tsx    From bitpay-browser-extension with MIT License 6 votes vote down vote up
Snack: React.FC<{ message: string; onClose: () => void }> = ({ message, onClose }) => (
  <div className="snack">
    <Snackbar style={position} open={!!message} autoHideDuration={6000} anchorOrigin={anchorOrigin} onClose={onClose}>
      <Alert
        style={menu}
        severity="error"
        action={
          <IconButton aria-label="close" color="inherit" size="small" style={icon} onClick={onClose}>
            <img src="/assets/icons/close.svg" alt="close" />
          </IconButton>
        }
      >
        <AlertTitle style={title}>Please Try Again!</AlertTitle>
        {message}
      </Alert>
    </Snackbar>
  </div>
)
Example #3
Source File: ToastMessage.tsx    From glific-frontend with GNU Affero General Public License v3.0 6 votes vote down vote up
ToastMessage: React.SFC<Props> = ({
  open = true,
  severity = 'success',
  message,
  handleClose,
  hideDuration = 5000,
}) => {
  const handleCloseButton = () => {
    handleClose(false);
  };

  return (
    <Snackbar
      anchorOrigin={{
        vertical: 'top',
        horizontal: 'center',
      }}
      classes={{ anchorOriginTopCenter: styles.SnackBar }}
      open={open}
      onClose={handleCloseButton}
      autoHideDuration={hideDuration}
    >
      <Alert
        severity={severity}
        icon={false}
        action={<CrossIcon onClick={handleCloseButton} data-testid="crossIcon" />}
        classes={{
          standardSuccess: styles.Success,
          standardWarning: styles.Warning,
          action: styles.Action,
          message: styles.Message,
        }}
      >
        {message}
      </Alert>
    </Snackbar>
  );
}
Example #4
Source File: Layout.tsx    From fishbowl with MIT License 6 votes vote down vote up
function Layout(props: { children: React.ReactNode }) {
  const [open, setOpen] = React.useState(false)
  const [message, setMessage] = React.useState<string | null>(null)

  const handleClose = () => setOpen(false)

  return (
    <Box>
      <Snackbar
        anchorOrigin={{ vertical: "top", horizontal: "center" }}
        open={open}
        onClose={handleClose}
        autoHideDuration={3000}
        message={message}
        TransitionComponent={GrowTransition}
        action={
          <IconButton
            size="small"
            aria-label="close"
            color="inherit"
            onClick={handleClose}
          >
            <CloseIcon fontSize="small" />
          </IconButton>
        }
      ></Snackbar>
      <NotificationContext.Provider
        value={{
          send: (message: string) => {
            setMessage(message)
            setOpen(true)
          },
        }}
      >
        {props.children}
      </NotificationContext.Provider>
    </Box>
  )
}
Example #5
Source File: Layout.tsx    From storefront with MIT License 6 votes vote down vote up
Layout: React.FC = ({ children }) => {
  const { alerts, closeModal, displayModal, modalView, removeAlert } = useUI();

  return (
    <>
      <Header />
      <Box sx={{ display: 'flex', flexDirection: 'column' }}>{children}</Box>
      <Footer />
      <CookieBanner />
      <Dialog open={displayModal} onClose={closeModal}>
        <>{modalView === 'LOGIN_VIEW' && <LoginView />}</>
      </Dialog>
      {alerts.map((alert) => (
        <Snackbar
          key={alert.id}
          autoHideDuration={5000}
          open={alert.open}
          onClose={() => removeAlert(alert.id)}
        >
          <Alert {...alert.props} onClose={() => removeAlert(alert.id)}>
            {alert.message}
          </Alert>
        </Snackbar>
      ))}
    </>
  );
}
Example #6
Source File: CodeSnippetWithCopy.tsx    From dashboard with Apache License 2.0 6 votes vote down vote up
export default function CodeSnippetWithCopy({ codeSnippet }: Props) {
  const [openFeedback, setOpenFeedback] = useState(false)
  const handleCopyClick = () => {
    copyToClipboard(codeSnippet)
    if (!openFeedback) setOpenFeedback(true)
  }
  const handleClose = () => {
    setOpenFeedback(false)
  }
  const action = (
    <>
      <IconButton
        size="small"
        aria-label="close"
        color="inherit"
        onClick={handleClose}
      >
        <CloseIcon fontSize="small" />
      </IconButton>
    </>
  )
  return (
    <div>
      <CodeContainer onClick={() => handleCopyClick()}>
        <CodeSnippetContainer>{codeSnippet}</CodeSnippetContainer>
        <CopyIcon src={Copy} />

        <Snackbar
          anchorOrigin={{ vertical: "top", horizontal: "right" }}
          open={openFeedback}
          autoHideDuration={1}
          action={action}
          message="Command copied"
        />
      </CodeContainer>
    </div>
  )
}
Example #7
Source File: AlertBar.tsx    From End-to-End-Web-Testing-with-Cypress with MIT License 6 votes vote down vote up
AlertBar: React.FC<Props> = ({ snackbarService }) => {
  const [snackbarState] = useService(snackbarService);

  return (
    <Snackbar
      anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
      open={snackbarState?.matches("visible")}
      autoHideDuration={3000}
    >
      <Alert
        data-test={`alert-bar-${snackbarState?.context.severity}`}
        elevation={6}
        variant="filled"
        severity={snackbarState?.context.severity}
      >
        {snackbarState?.context.message}
      </Alert>
    </Snackbar>
  );
}
Example #8
Source File: SnackBar.tsx    From postgres-nest-react-typescript-boilerplate with GNU General Public License v3.0 6 votes vote down vote up
Toast: FC<{ position: SnackbarOrigin; duration: number }> = ({
  position: { vertical, horizontal },
  duration
}) => {
  const snackBarState = useSelector((state: IStore) => state.ui.snackbar);

  const dispatch = useDispatch();

  const [isOpen, setIsOpen] = useState<boolean>(false);
  const [msg, setMsg] = useState<string>(snackBarState.msg);
  const [type, setType] = useState<AlertType>(snackBarState.type);

  const onCloseHandler = () => {
    dispatch(clearSnackBar());
  };

  useEffect(() => {
    if (!snackBarState.msg) {
      setIsOpen(false);
    } else {
      setIsOpen(true);
      setType(snackBarState.type);
      setMsg(snackBarState.msg);
    }
  }, [snackBarState]);

  return (
    <Snackbar
      onClose={onCloseHandler}
      open={isOpen}
      autoHideDuration={duration}
      anchorOrigin={{ vertical, horizontal }}
    >
      <MuiAlert elevation={6} variant="filled" severity={type}>
        {msg}
      </MuiAlert>
    </Snackbar>
  );
}
Example #9
Source File: useSnackbarAlert.tsx    From shadowsocks-electron with GNU General Public License v3.0 6 votes vote down vote up
useSnackbarAlert = (props?: SnackbarAlertProps): [React.FunctionComponent, SetMessage] => {
  const styles = useStyles();
  const [message, setMessage] = useState('');
  return [
    (
      React.memo((() =>
        <Snackbar
          className={styles.snackbar}
          anchorOrigin={{
            vertical: props?.vertical || "top",
            horizontal: props?.horizontal || "right"
          }}
          TransitionComponent={transition}
          open={!!message}
          autoHideDuration={props?.duration || 1e3}
          onClose={() => setMessage('')}
          message={message}
        />))
    ),
    (msg: string) => setTimeout(() => setMessage(msg), .5e3)
  ];
}
Example #10
Source File: FeedbackModal.tsx    From SeeQR with MIT License 6 votes vote down vote up
FeedbackModal = () => {
  const [isOpen, setOpen] = useState(false);
  const [message, setMessage] = useState('');
  const [severity, setSeverity] = useState<FeedbackSeverity>('info');

  useEffect(() => {
    const receiveFeedback = (evt: IpcRendererEvent, feedback: Feedback) => {
      const validTypes: FeedbackSeverity[] = ['success','error', 'info', 'warning'];
      // Ignore 'success' feedback.
      if (validTypes.includes(feedback.type)) {
        setSeverity(feedback.type);
        setMessage(feedback.message?.toString() ?? 'ERROR: Operation Failed');
        setOpen(true);
      }
    };
    ipcRenderer.on('feedback', receiveFeedback);
    return () => {
      ipcRenderer.removeListener('feedback', receiveFeedback);
    };
  });

  const handleClose = () => setOpen(false);

  return (
    <Snackbar
      open={isOpen}
      onClose={handleClose}
      autoHideDuration={readingTime(message)}
      anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
      // disable hiding on clickAway
      ClickAwayListenerProps={{ onClickAway: () => {} }}
    >
      <Alert onClose={handleClose} severity={severity}>
        {message}
      </Alert>
    </Snackbar>
  );
}
Example #11
Source File: SnackbarProvider.tsx    From cards-against-formality-pwa with BSD 2-Clause "Simplified" License 6 votes vote down vote up
export default function ThemeProvider({ children }: any) {
  const [message, setMessage] = useState<SnackbarMessage | null>(null)

  const onClose = useCallback(() => {
    setMessage(null);
  }, []);

  const openSnack = useCallback((data: SnackbarMessage | null) => {
    setMessage(data);
  }, []);

  useEffect(() => {
    let timeout: NodeJS.Timeout;
    if (message) {
      timeout = setTimeout(() => {
        onClose();
      }, message.autoHideDuration ? message.autoHideDuration : 3000)
    }

    return () => {
      if (timeout) {
        clearTimeout(timeout);
      }
    }
  }, [message, onClose])

  return <SnackbarContext.Provider value={{ message, openSnack }}>
    <Snackbar open={!!message}>
      {message ? <MuiAlert severity={message?.severity} elevation={6} variant="filled" >
        {message?.text}
      </MuiAlert> : null as any}
    </Snackbar>
    {children}
  </SnackbarContext.Provider >;
}
Example #12
Source File: Toast.tsx    From knboard with MIT License 5 votes vote down vote up
Toast = () => {
  const dispatch = useDispatch();

  const open = useSelector((state: RootState) => state.toast.open);
  const message = useSelector((state: RootState) => state.toast.message);
  const severity = useSelector((state: RootState) => state.toast.severity);

  let timer: ReturnType<typeof setTimeout>;

  function handleClose() {
    dispatch(clearToast());
  }

  useEffect(() => {
    if (open) {
      timer = setTimeout(() => {
        handleClose();
      }, TOAST_AUTO_HIDE_DURATION);
    }
    return () => clearTimeout(timer);
  }, [open]);

  return (
    <Snackbar open={open}>
      <Alert
        severity={severity}
        action={
          <Button
            color="inherit"
            size="small"
            onClick={handleClose}
            data-testid="toast-close"
            css={css`
              min-width: 0;
            `}
          >
            <FontAwesomeIcon icon={faTimes} />
          </Button>
        }
      >
        {message}
      </Alert>
    </Snackbar>
  );
}
Example #13
Source File: Todos.tsx    From max-todos with MIT License 5 votes vote down vote up
Todos = () => {
  const { todos, moveTodo } = useContext(MainContext)!;
  const [deleteSnackOpen, setDeleteSnackOpen] = useState(false);
  const [editSnackOpen, setEditSnackOpen] = useState(false);
  const [dragging, setDragging] = useState(false);
  const onDragEnd = (x: DropResult) => {
    if (!x.destination) return console.log(x);
    moveTodo(x.source.index, x.destination.index);
    setTimeout(() => setDragging(false), 200);
  };
  return (
    <>
      <DragDropContext
        onBeforeDragStart={() => setDragging(true)}
        onDragEnd={onDragEnd}
      >
        <Droppable droppableId="0">
          {(p) => (
            <div {...p.droppableProps} ref={p.innerRef}>
              <FlipMove disableAllAnimations={dragging}>
                {todos.map((todo, i) => {
                  return (
                    <Todo
                      todo={todo}
                      key={todo.id}
                      onDelete={() => setDeleteSnackOpen(true)}
                      index={i}
                      onEdit={() => setEditSnackOpen(true)}
                    />
                  );
                })}
              </FlipMove>
              {p.placeholder}
            </div>
          )}
        </Droppable>
      </DragDropContext>

      <Snackbar
        open={deleteSnackOpen}
        autoHideDuration={4000}
        onClose={() => setDeleteSnackOpen(false)}
        anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
      >
        <Alert
          elevation={6}
          variant="filled"
          onClose={() => setDeleteSnackOpen(false)}
          severity="success"
        >
          Successfully deleted item!
        </Alert>
      </Snackbar>
      <Snackbar
        open={editSnackOpen}
        autoHideDuration={4000}
        onClose={() => setEditSnackOpen(false)}
        anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
      >
        <Alert
          elevation={6}
          variant="filled"
          onClose={() => setEditSnackOpen(false)}
          severity="success"
        >
          Successfully edited item!
        </Alert>
      </Snackbar>
    </>
  );
}
Example #14
Source File: AddTodo.tsx    From max-todos with MIT License 5 votes vote down vote up
AddTodo: FC<{ addTodo: (text: string) => void }> = ({ addTodo }) => {
  const [text, setText] = useState("");
  const [open, setOpen] = useState(false);
  const handleChange = (
    e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
  ) => setText(e.target.value);
  const createTodo = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    addTodo(text);
    setText("");
    if (text.trim()) setOpen(true);
  };

  return (
    <div>
      <Container maxWidth="sm">
        <form onSubmit={createTodo} className="add-todo">
          <FormControl fullWidth={true}>
            <TextField
              label="I will do this"
              variant="standard"
              onChange={handleChange}
              required={true}
              value={text}
            />
            <Button
              variant="contained"
              color="primary"
              style={{ marginTop: 5 }}
              type="submit"
            >
              <Add />
              Add
            </Button>
          </FormControl>
        </form>
      </Container>
      <Snackbar
        open={open}
        autoHideDuration={4000}
        onClose={() => setOpen(false)}
        anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
      >
        <Alert
          // icon={<Check fontSize="inherit" />}
          elevation={6}
          variant="filled"
          onClose={() => setOpen(false)}
          severity="success"
        >
          Successfully added item!
        </Alert>
      </Snackbar>
    </div>
  );
}
Example #15
Source File: RestartSnackbar.tsx    From TidGi-Desktop with Mozilla Public License 2.0 5 votes vote down vote up
export function useRestartSnackbar(waitBeforeCountDown = 1000, waitBeforeRestart = 10_000): [() => void, JSX.Element] {
  const { t } = useTranslation();
  const [opened, openedSetter] = useState(false);
  const [inCountDown, inCountDownSetter] = useState(false);
  const [currentWaitBeforeRestart, currentWaitBeforeRestartSetter] = useState(waitBeforeRestart);

  const handleCloseAndRestart = useCallback(() => {
    openedSetter(false);
    inCountDownSetter(false);
    void window.service.window.requestRestart();
  }, [openedSetter]);

  const handleCancelRestart = useCallback(() => {
    openedSetter(false);
    inCountDownSetter(false);
  }, [openedSetter]);

  const startRestartCountDown = useDebouncedCallback(
    () => {
      inCountDownSetter(true);
      openedSetter(true);
    },
    [openedSetter, inCountDown, inCountDownSetter],
    waitBeforeCountDown,
    { leading: false },
  );

  const requestRestartCountDown = useCallback(() => {
    if (inCountDown) {
      // if already started,refresh count down of autoHideDuration, so the count down will rerun
      // so if user is editing userName in the config, count down will refresh on each onChange of Input
      currentWaitBeforeRestartSetter(currentWaitBeforeRestart + 1);
    } else {
      // of not started, we try start it
      startRestartCountDown();
    }
  }, [inCountDown, currentWaitBeforeRestart, startRestartCountDown]);

  return [
    requestRestartCountDown,
    <div key="RestartSnackbar">
      <Snackbar
        anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
        open={opened}
        onClose={(event, reason) => {
          switch (reason) {
            case 'timeout': {
              handleCloseAndRestart();
              break;
            }
            case 'clickaway': {
              handleCancelRestart();
              break;
            }
          }
        }}
        message={t('Dialog.RestartMessage')}
        autoHideDuration={currentWaitBeforeRestart}
        action={
          <>
            <RestartButton
              key={currentWaitBeforeRestart}
              currentWaitBeforeRestart={currentWaitBeforeRestart}
              color="secondary"
              size="small"
              onClick={handleCloseAndRestart}>
              {t('Dialog.RestartNow')}
            </RestartButton>
            <Tooltip title={<span>{t('Dialog.Later')}</span>}>
              <IconButton size="small" aria-label="close" color="inherit" onClick={handleCancelRestart}>
                <CloseIcon fontSize="small" />
              </IconButton>
            </Tooltip>
          </>
        }
      />
    </div>,
  ];
}
Example #16
Source File: CloneWikiDoneButton.tsx    From TidGi-Desktop with Mozilla Public License 2.0 5 votes vote down vote up
export function CloneWikiDoneButton({ form, isCreateMainWorkspace, errorInWhichComponentSetter }: IWikiWorkspaceFormProps): JSX.Element {
  const { t } = useTranslation();
  const [hasError, wikiCreationMessage, wikiCreationMessageSetter, hasErrorSetter] = useValidateCloneWiki(
    isCreateMainWorkspace,
    form,
    errorInWhichComponentSetter,
  );
  const onSubmit = useCloneWiki(isCreateMainWorkspace, form, wikiCreationMessageSetter, hasErrorSetter, errorInWhichComponentSetter);
  const [logPanelOpened, logPanelSetter, inProgressOrError] = useWikiCreationProgress(wikiCreationMessageSetter, wikiCreationMessage, hasError);
  if (hasError) {
    return (
      <>
        <CloseButton variant="contained" disabled>
          {wikiCreationMessage}
        </CloseButton>
        {wikiCreationMessage !== undefined && <ReportErrorFabButton message={wikiCreationMessage} />}
      </>
    );
  }
  return (
    <>
      {inProgressOrError && <LinearProgress color="secondary" />}
      <Snackbar open={logPanelOpened} autoHideDuration={5000} onClose={() => logPanelSetter(false)}>
        <Alert severity="info">{wikiCreationMessage}</Alert>
      </Snackbar>

      {isCreateMainWorkspace ? (
        <CloseButton variant="contained" color="secondary" disabled={inProgressOrError} onClick={onSubmit}>
          <Typography variant="body1" display="inline">
            {t('AddWorkspace.CloneWiki')}
          </Typography>
          <WikiLocation>{form.wikiFolderLocation}</WikiLocation>
        </CloseButton>
      ) : (
        <CloseButton variant="contained" color="secondary" disabled={inProgressOrError} onClick={onSubmit}>
          <Typography variant="body1" display="inline">
            {t('AddWorkspace.CloneWiki')}
          </Typography>
          <WikiLocation>{form.wikiFolderLocation}</WikiLocation>
          <Typography variant="body1" display="inline">
            {t('AddWorkspace.AndLinkToMainWorkspace')}
          </Typography>
        </CloseButton>
      )}
    </>
  );
}
Example #17
Source File: ExistedWikiDoneButton.tsx    From TidGi-Desktop with Mozilla Public License 2.0 5 votes vote down vote up
export function ExistedWikiDoneButton({
  form,
  isCreateMainWorkspace,
  isCreateSyncedWorkspace,
  errorInWhichComponentSetter,
}: IWikiWorkspaceFormProps & { isCreateMainWorkspace: boolean; isCreateSyncedWorkspace: boolean }): JSX.Element {
  const { t } = useTranslation();
  const [hasError, wikiCreationMessage, wikiCreationMessageSetter, hasErrorSetter] = useValidateExistedWiki(
    isCreateMainWorkspace,
    isCreateSyncedWorkspace,
    form,
    errorInWhichComponentSetter,
  );
  const onSubmit = useExistedWiki(isCreateMainWorkspace, isCreateSyncedWorkspace, form, wikiCreationMessageSetter, hasErrorSetter, errorInWhichComponentSetter);
  const [logPanelOpened, logPanelSetter, inProgressOrError] = useWikiCreationProgress(wikiCreationMessageSetter, wikiCreationMessage, hasError);
  if (hasError) {
    return (
      <>
        <CloseButton variant="contained" disabled>
          {wikiCreationMessage}
        </CloseButton>
        {wikiCreationMessage !== undefined && <ReportErrorFabButton message={wikiCreationMessage} />}
      </>
    );
  }
  return (
    <>
      {inProgressOrError && <LinearProgress color="secondary" />}
      <Snackbar open={logPanelOpened} autoHideDuration={5000} onClose={() => logPanelSetter(false)}>
        <Alert severity="info">{wikiCreationMessage}</Alert>
      </Snackbar>

      {isCreateMainWorkspace ? (
        <CloseButton variant="contained" color="secondary" disabled={inProgressOrError} onClick={onSubmit}>
          <Typography variant="body1" display="inline">
            {t('AddWorkspace.ImportWiki')}
          </Typography>
          <WikiLocation>{form.wikiFolderLocation}</WikiLocation>
        </CloseButton>
      ) : (
        <CloseButton variant="contained" color="secondary" disabled={inProgressOrError} onClick={onSubmit}>
          <Typography variant="body1" display="inline">
            {t('AddWorkspace.ImportWiki')}
          </Typography>
          <WikiLocation>{form.wikiFolderLocation}</WikiLocation>
          <Typography variant="body1" display="inline">
            {t('AddWorkspace.AndLinkToMainWorkspace')}
          </Typography>
        </CloseButton>
      )}
    </>
  );
}
Example #18
Source File: index.tsx    From uno-game with MIT License 5 votes vote down vote up
NotificationBar: React.FC = () => {
	const [opened, setOpened] = useState(false)

	const classes = useStyles({ opened })

	SocketService.on("connect", () => {
		setOpened(false)
	})

	SocketService.on("disconnect", () => {
		setOpened(true)
	})

	const message = "Your disconnected. Reconnecting..."

	if (Device.isMobile) {
		return (
			<Snackbar
				open={opened}
				anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
			>
				<Alert severity="error">
					{message}
				</Alert>
			</Snackbar>
		)
	}

	return (
		<Grid
			container
			alignItems="center"
			justify="center"
			className={classes.container}
		>
			<Typography
				variant="h2"
				color="textSecondary"
				className={classes.text}
			>
				{message}
			</Typography>
		</Grid>
	)
}
Example #19
Source File: NewWikiDoneButton.tsx    From TidGi-Desktop with Mozilla Public License 2.0 5 votes vote down vote up
export function NewWikiDoneButton({
  form,
  isCreateMainWorkspace,
  isCreateSyncedWorkspace,
  errorInWhichComponentSetter,
}: IWikiWorkspaceFormProps & { isCreateMainWorkspace: boolean; isCreateSyncedWorkspace: boolean }): JSX.Element {
  const { t } = useTranslation();
  const [hasError, wikiCreationMessage, wikiCreationMessageSetter, hasErrorSetter] = useValidateNewWiki(
    isCreateMainWorkspace,
    isCreateSyncedWorkspace,
    form,
    errorInWhichComponentSetter,
  );
  const onSubmit = useNewWiki(isCreateMainWorkspace, isCreateSyncedWorkspace, form, wikiCreationMessageSetter, hasErrorSetter, errorInWhichComponentSetter);
  const [logPanelOpened, logPanelSetter, inProgressOrError] = useWikiCreationProgress(wikiCreationMessageSetter, wikiCreationMessage, hasError);
  if (hasError) {
    return (
      <>
        <CloseButton variant="contained" disabled={true}>
          {wikiCreationMessage}
        </CloseButton>
        {wikiCreationMessage !== undefined && <ReportErrorFabButton message={wikiCreationMessage} />}
      </>
    );
  }
  return (
    <>
      {inProgressOrError && <LinearProgress color="secondary" />}
      <Snackbar open={logPanelOpened} autoHideDuration={5000} onClose={() => logPanelSetter(false)}>
        <Alert severity="info">{wikiCreationMessage}</Alert>
      </Snackbar>

      {isCreateMainWorkspace ? (
        <CloseButton variant="contained" color="secondary" disabled={inProgressOrError} onClick={onSubmit}>
          <Typography variant="body1" display="inline">
            {t('AddWorkspace.CreateWiki')}
          </Typography>
          <WikiLocation>{form.wikiFolderLocation}</WikiLocation>
        </CloseButton>
      ) : (
        <CloseButton variant="contained" color="secondary" disabled={inProgressOrError} onClick={onSubmit}>
          <Typography variant="body1" display="inline">
            {t('AddWorkspace.CreateWiki')}
          </Typography>
          <WikiLocation>{form.wikiFolderLocation}</WikiLocation>
          <Typography variant="body1" display="inline">
            {t('AddWorkspace.AndLinkToMainWorkspace')}
          </Typography>
        </CloseButton>
      )}
    </>
  );
}
Example #20
Source File: Snack.tsx    From firetable with Apache License 2.0 5 votes vote down vote up
export default function Snack() {
  const classes = useStyles();

  const {
    position,
    isOpen,
    close,
    message,
    duration,
    action,
    variant,
    progress,
  } = useSnackContext();

  if (variant === "progress")
    return (
      <Snackbar
        anchorOrigin={position}
        open={isOpen}
        onClose={close}
        message={message}
        action={
          <>
            <span className={classes.progressText}>
              {progress.value}
              {progress.target && `/${progress.target}`}
            </span>

            <CircularProgress
              variant={progress.value ? "static" : "indeterminate"}
              value={
                progress.target
                  ? (progress.value / progress.target) * 100
                  : progress.value
              }
              size={24}
              className={classes.progress}
            />
          </>
        }
        ContentProps={{ classes: { action: classes.progressAction } }}
        // Stop closing when user clicks
        ClickAwayListenerProps={{ mouseEvent: false }}
      />
    );

  if (!variant)
    return (
      <Snackbar
        anchorOrigin={position}
        open={isOpen}
        onClose={close}
        autoHideDuration={duration}
        message={message}
        action={action}
        ClickAwayListenerProps={{ mouseEvent: false }}
        onClick={close}
      />
    );

  return (
    <Snackbar
      anchorOrigin={position}
      open={isOpen}
      onClose={close}
      autoHideDuration={duration}
      ClickAwayListenerProps={{ mouseEvent: false }}
      onClick={close}
    >
      <Alert
        variant="filled"
        action={action}
        severity={variant}
        classes={{ icon: classes.alertIcon, message: classes.alertMessage }}
      >
        {message}
      </Alert>
    </Snackbar>
  );
}
Example #21
Source File: index.tsx    From back-home-safe with GNU General Public License v3.0 5 votes vote down vote up
Login = () => {
  const { t } = useTranslation("login");
  const [password, setPassword] = useState("");
  const { unlockStore } = useData();
  const [showPasswordError, setShowPasswordError] = useState(false);

  const handleLogin = () => {
    const success = unlockStore(password);

    if (!success) {
      setShowPasswordError(true);
      setPassword("");
    }
  };

  return (
    <PageWrapper>
      <Wrapper>
        <Unlock />
        <div>{t("message.please_input_password")}</div>
        <InputWrapper
          onSubmit={(e) => {
            e.preventDefault();
            handleLogin();
          }}
        >
          <TextField
            type="password"
            autoComplete="current-password"
            value={password}
            onChange={(e) => {
              setPassword(e.target.value);
            }}
          />
          <ButtonWrapper>
            <Button
              variant="contained"
              color="secondary"
              disabled={isEmpty(password)}
              type="submit"
            >
              {t("global:button.unlock")}
            </Button>
          </ButtonWrapper>
        </InputWrapper>
        <Button onClick={clearAllData}>{t("button.reset")}</Button>
      </Wrapper>
      <Snackbar
        open={showPasswordError}
        autoHideDuration={2000}
        onClose={() => {
          setShowPasswordError(false);
        }}
        anchorOrigin={{ vertical: "top", horizontal: "center" }}
      >
        <Alert elevation={6} variant="filled" severity="error">
          {t("message.wrong_password")}
        </Alert>
      </Snackbar>
    </PageWrapper>
  );
}
Example #22
Source File: SnackbarNotification.tsx    From firebase-react-typescript-project-template with MIT License 5 votes vote down vote up
SnackbarNotification = () => {
  const classes = useStyles({});
  const {
    isOpen,
    message,
    variant,
    closeNotification,
  } = useSnackbarNotification();

  return (
    <Snackbar
      anchorOrigin={{
        vertical: "bottom",
        horizontal: "center",
      }}
      TransitionComponent={SlideTransition}
      open={isOpen}
      autoHideDuration={5000}
      onClose={closeNotification}
      className={classes.root}
    >
      <SnackbarContent
        classes={{
          root: clsx(classes.snackbarContent, classes[variant]),
          message: classes.message,
        }}
        message={
          <>
            <Typography variant="body1" className={classes.messageText}>
              {message}
            </Typography>
            <IconButton
              key="close"
              color="inherit"
              onClick={closeNotification}
              className={classes.closeButton}
            >
              <X />
            </IconButton>
          </>
        }
      />
    </Snackbar>
  );
}
Example #23
Source File: Header.tsx    From safe-airdrop with MIT License 5 votes vote down vote up
Header = (): JSX.Element => {
  const { messages, showMessages, hideMessages, toggleMessages, removeMessage } = useContext(MessageContext);

  const handleClose = (event: React.SyntheticEvent | Event, reason?: string) => {
    if (reason === "clickaway") {
      return;
    }

    hideMessages();
  };

  return (
    <HeaderContainer>
      <Fab
        variant="circular"
        size="small"
        className={messages.length === 0 ? "statusDotButtonEmpty" : "statusDotButtonErrors"}
        style={{ textTransform: "none", width: "34px", height: "34px" }}
        onClick={toggleMessages}
      >
        {messages.length === 0 ? (
          <Icon color="white" type="check" size="sm" />
        ) : (
          <Text size="xl" color="white">
            {messages.length}
          </Text>
        )}
      </Fab>
      <FAQModal />
      <Snackbar
        anchorOrigin={{ vertical: "top", horizontal: "right" }}
        open={showMessages}
        onClose={handleClose}
        autoHideDuration={6000}
        style={{ gap: "4px", top: "64px" }}
      >
        <AlertWrapper>
          {messages.length === 0 && (
            <Alert secerity="success" key="successMessage">
              No warnings or errors.
            </Alert>
          )}
          {messages.map((message: Message, index: number) => (
            <Alert severity={message.severity} key={"message" + index} onClose={() => removeMessage(message)}>
              {message.message}
            </Alert>
          ))}
        </AlertWrapper>
      </Snackbar>
    </HeaderContainer>
  );
}
Example #24
Source File: MetaMaskConnector.tsx    From metamask-snap-polkadot with Apache License 2.0 5 votes vote down vote up
MetaMaskConnector = () => {

    const [state, dispatch] = useContext(MetaMaskContext);

    useEffect( () => {
        (async () => {
            if(await isPolkadotSnapInstalled()) {
                dispatch({type: MetamaskActions.SET_INSTALLED_STATUS, payload: {isInstalled: true}});
            }
        })();
    }, [dispatch]);

    const installSnap = useCallback(async () => {
       const isInitiated = await installPolkadotSnap();
       if(!isInitiated) {
           dispatch({type: MetamaskActions.SET_INSTALLED_STATUS, payload: {isInstalled: false, message: "Please accept snap installation prompt"}})
       } else {
           dispatch({type: MetamaskActions.SET_INSTALLED_STATUS, payload: {isInstalled: true}});
       }
    }, [dispatch]);

    const handleClose = (event: React.SyntheticEvent | React.MouseEvent, reason?: string) => {
        if (reason === 'clickaway') {
          return;
        }
        dispatch({type: MetamaskActions.SET_INSTALLED_STATUS, payload: false})
      };
    
    const shouldDisplaySnackbar = (): boolean => {
      if (!state.polkadotSnap.isInstalled && state.polkadotSnap.message) return true;
      else return false;
    }

    return(
        <div>
            <Snackbar
                anchorOrigin={{
                    vertical: 'bottom',
                    horizontal: 'left',
                }}
                open={shouldDisplaySnackbar()}
                autoHideDuration={6000}
                onClose={handleClose}
                message={state.polkadotSnap.message}
                action={
                    <React.Fragment>
                      <IconButton size="small" aria-label="close" color="inherit" onClick={handleClose}>
                        <CloseIcon fontSize="small" />
                      </IconButton>
                    </React.Fragment>
                  }
            />
            <Hidden xsUp={state.hasMetaMask}>
                <Alert severity="warning">Ensure that MetaMask is installed!</Alert>
                <Box mt={"1rem"} />
            </Hidden>
            <Button
                disabled={!state.hasMetaMask}
                onClick={installSnap}
                variant="contained"
                size={"large"}
                color="primary"
            >
                Connect to MetaMask
            </Button>
        </div>
    );

}
Example #25
Source File: App.tsx    From DamnVulnerableCryptoApp with MIT License 5 votes vote down vote up
App = () => {


  const p = ProgressService.createOrGet();

  const [progress, setProgress] = useState(p);
  const [challengesDone, setChallengesDone] = useState(ProgressService.done());
  const [progressPercentage, setProgressPercentage] = useState(ProgressService.donePercentage());
  const [loading, setLoading] = useState(false);
  const [snackOpen, setSnackOpen] = useState(false);
  const [snackErrorMessage, _setSnackErrorMessage] = useState("");

  const setSnackErrorMessage = (msg: string) => {
    _setSnackErrorMessage(msg);
    setSnackOpen(true);
  };

  const layoutInitialState = {
    progress, setProgress,
    progressPercentage, setProgressPercentage,
    challengesDone, setChallengesDone,
    loading, setLoading,
    setSnackErrorMessage
  };

  const renderChallenge = (c: ChallengeData) => {
    return () => <Challenge obj={c} />;
  };



  const snackClose = () => {
    setSnackOpen(false);
  };

  return (
    <div className="App">
      <Router>
        <LayoutContext.Provider value={layoutInitialState}>
          <Appbar />
          <Container fixed>
            {
              Challenges.map((c) => {
                return (
                  <Box key={c.name}>
                    <Route path={c.url} render={renderChallenge(c)} exact={true} />
                  </Box>
                );
              })
            }
            <Route exact path="/"><Dashboard /></Route>
            <Route exact path="/docs/:topic" component={Documentation} />

            <Snackbar open={snackOpen} autoHideDuration={6000} onClose={snackClose}>
              <Alert severity="error">
                {snackErrorMessage}
              </Alert>
            </Snackbar>
          </Container>
        </LayoutContext.Provider>
      </Router>

    </div >
  );
}
Example #26
Source File: index.tsx    From aqualink-app with MIT License 5 votes vote down vote up
StatusSnackbar = ({
  open,
  message,
  furtherActionLabel,
  severity,
  handleClose,
  onFurtherActionTake,
}: StatusSnackbarProps) => {
  const classes = useStyles(!!message);

  return message ? (
    <Snackbar
      className={classes.snackbar}
      open={open}
      onClose={handleClose}
      anchorOrigin={{
        vertical: "bottom",
        horizontal: "left",
      }}
    >
      <Alert
        className={classes.alert}
        variant="filled"
        onClose={handleClose}
        severity={severity}
        classes={{ message: classes.alertMessage }}
      >
        {message}
        {furtherActionLabel && onFurtherActionTake && (
          <Button
            size="small"
            className={classes.button}
            onClick={onFurtherActionTake}
          >
            {furtherActionLabel}
          </Button>
        )}
      </Alert>
    </Snackbar>
  ) : null;
}
Example #27
Source File: tl-layer.tsx    From mtcute with GNU Lesser General Public License v3.0 4 votes vote down vote up
export default function TlLayer({
    data: { layer, prev, next },
}: {
    data: GraphqlResult
}) {
    const pageClasses = usePageStyles()
    const classes = useStyles()

    const [snackText, setSnackText] = useState<string | undefined>(undefined)

    function copyToClipboard() {
        // https://stackoverflow.com/a/30810322
        const area = document.createElement('textarea')
        area.style.position = 'fixed'
        area.style.top = '0'
        area.style.left = '0'
        area.style.width = '2em'
        area.style.height = '2em'
        area.style.padding = '0'
        area.style.border = 'none'
        area.style.outline = 'none'
        area.style.boxShadow = 'none'
        area.style.background = 'transparent'

        area.value = layer.content

        document.body.appendChild(area)
        area.focus()
        area.select()

        document.execCommand('copy')
        document.body.removeChild(area)

        setSnackText('Copied to clipboard!')
    }

    function downloadAsFile() {
        const link = document.createElement('a')
        link.setAttribute(
            'href',
            'data:text/plain;charset=utf-8,' + encodeURIComponent(layer.content)
        )
        link.setAttribute(
            'download',
            `layer${layer.layer}${layer.rev ? `-rev${layer.rev}` : ''}.tl`
        )

        link.style.display = 'none'
        document.body.appendChild(link)
        link.click()
        document.body.removeChild(link)
    }

    return (
        <Page>
            <Helmet>
                <title>
                    {`Layer ${layer.layer}` +
                        `${layer.rev > 0 ? ` rev. ${layer.rev}` : ''}`}
                </title>
                <meta
                    name="description"
                    content={
                        `TL code representing layer ${layer.layer}` +
                        `${layer.rev > 0 && ` rev. ${layer.rev}`}` +
                        ` (from ${
                            layer.source.website ? 'website' : layer.source.date
                        })`
                    }
                />
            </Helmet>

            <div className={classes.navigation}>
                {prev && (
                    <Button
                        component={Link}
                        variant="outlined"
                        color="primary"
                        to={`/history/layer${prev.layer}${
                            prev.rev ? `-rev${prev.rev}` : ''
                        }`}
                        startIcon={<ChevronLeftIcon />}
                    >
                        Layer {prev.layer}
                        {prev.rev > 0 && ` rev. ${prev.rev}`}
                    </Button>
                )}
                <Spacer />
                {next && (
                    <Button
                        component={Link}
                        variant="outlined"
                        color="primary"
                        to={`/history/layer${next.layer}${
                            next.rev ? `-rev${next.rev}` : ''
                        }`}
                        endIcon={<ChevronRightIcon />}
                    >
                        Layer {next.layer}
                        {next.rev > 0 && ` rev. ${next.rev}`}
                    </Button>
                )}
            </div>

            <div className={pageClasses.heading0}>
                <Breadcrumbs>
                    <MuiLink component={Link} to={`/history`}>
                        History
                    </MuiLink>
                    <Typography color="textPrimary">
                        Layer {layer.layer}
                        {layer.rev > 0 && ` rev. ${layer.rev}`}
                    </Typography>
                </Breadcrumbs>
                <Typography variant="h3" id="title">
                    Layer {layer.layer}
                    {layer.rev > 0 && (
                        <span className={pageClasses.rev}>
                            {' '}
                            rev. {layer.rev}
                        </span>
                    )}
                </Typography>
                <Typography variant="body2">
                    from {layer.source.website ? 'website' : layer.source.date}
                    {!layer.source.website && (
                        <>
                            {' '}
                            / commit{' '}
                            <MuiLink
                                href={`https://github.com/telegramdesktop/tdesktop/commit/${layer.source.commit}`}
                                target="_blank"
                            >
                                {layer.source.commit.substr(0, 7)}
                            </MuiLink>{' '}
                            (
                            <MuiLink
                                href={`https://github.com/telegramdesktop/tdesktop/blob/${layer.source.commit}/${layer.source.file}`}
                                target="_blank"
                            >
                                file
                            </MuiLink>
                            )
                        </>
                    )}
                </Typography>
            </div>

            <Snackbar
                anchorOrigin={{
                    vertical: 'top',
                    horizontal: 'right',
                }}
                open={snackText !== undefined}
                autoHideDuration={5000}
                onClose={() => setSnackText(undefined)}
                message={snackText}
            />
            <div
                className={classes.navigation}
                style={{ justifyContent: 'flex-end' }}
            >
                <Button
                    className={classes.btn}
                    variant="outlined"
                    color="primary"
                    startIcon={<CodeIcon />}
                    onClick={copyToClipboard}
                >
                    Copy to clipboard
                </Button>
                <Button
                    className={classes.btn}
                    variant="outlined"
                    color="primary"
                    startIcon={<CloudDownloadIcon />}
                    onClick={downloadAsFile}
                >
                    Download
                </Button>
            </div>

            <TlSchemaCode tl={layer.content} />
        </Page>
    )
}
Example #28
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 #29
Source File: AdrHeader.tsx    From log4brains with Apache License 2.0 4 votes vote down vote up
export function AdrHeader({
  className,
  adr,
  locallyEditable = false
}: AdrHeaderProps) {
  const classes = useStyles();

  const [linkCopiedSnackIsOpened, linkCopiedSnackSetOpened] = React.useState(
    false
  );
  const linkCopiedSnackClose = () => {
    linkCopiedSnackSetOpened(false);
  };

  const decidersIcon =
    adr.deciders.length > 1 ? (
      <PeopleIcon className={classes.icon} fontSize="inherit" />
    ) : (
      <PersonIcon className={classes.icon} fontSize="inherit" />
    );

  return (
    <>
      <div className={clsx(className, classes.root)}>
        <div>
          <div className={classes.inlineInfo}>
            {adr.package ? (
              <Typography variant="body2" title="Package">
                <CropFreeIcon className={classes.icon} fontSize="inherit" />{" "}
                {adr.package}
              </Typography>
            ) : null}

            <Typography
              variant="body2"
              title={adr.publicationDate ? "Publication date" : "Creation date"}
            >
              <EventIcon className={classes.icon} fontSize="inherit" />{" "}
              {moment(adr.publicationDate || adr.creationDate).format("ll")}
            </Typography>

            <div title="Status">
              <AdrStatusChip status={adr.status} />
            </div>
          </div>

          <Typography
            variant="body2"
            title={
              adr.deciders.length > 0
                ? `Decider${adr.deciders.length > 1 ? "s" : ""}`
                : "Author"
            }
          >
            {decidersIcon}{" "}
            {adr.deciders.length > 0
              ? adr.deciders.join(", ")
              : adr.lastEditAuthor}
          </Typography>

          {adr.tags.length > 0 ? (
            <Typography variant="body2" title="Tags">
              <LabelIcon className={classes.icon} fontSize="inherit" />{" "}
              <span className={classes.tags}>
                {adr.tags.map((tag) => `#${tag}`).join(" ")}
              </span>
            </Typography>
          ) : null}
        </div>
        <div>
          <div>
            <ButtonGroup size="medium">
              <Tooltip title="Copy link">
                <Button
                  onClick={() => {
                    copyTextToClipboard(
                      window.location.href.replace(window.location.hash, "")
                    );
                    linkCopiedSnackSetOpened(true);
                  }}
                >
                  <SvgIcon>
                    <LinkRIcon />
                  </SvgIcon>
                </Button>
              </Tooltip>

              {adr.repository ? (
                <Tooltip
                  title={`View/edit on ${
                    adr.repository.provider === "generic"
                      ? "Git"
                      : capitalize(adr.repository.provider)
                  }`}
                >
                  <Button
                    href={adr.repository.viewUrl}
                    target="_blank"
                    rel="noopener"
                  >
                    {getRepositoryIcon(adr.repository.provider)}
                  </Button>
                </Tooltip>
              ) : null}

              {locallyEditable ? (
                <Tooltip title="Edit locally">
                  <Button
                    color="secondary"
                    onClick={() => editLocally(adr.slug)}
                  >
                    <EditIcon />
                  </Button>
                </Tooltip>
              ) : null}
            </ButtonGroup>
          </div>
        </div>
      </div>
      <Snackbar
        anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
        open={linkCopiedSnackIsOpened}
        onClose={linkCopiedSnackClose}
        autoHideDuration={6000}
        message="Link copied to clipboard"
        action={
          <IconButton
            size="small"
            aria-label="close"
            color="inherit"
            onClick={linkCopiedSnackClose}
            title="Close"
          >
            <CloseIcon fontSize="small" />
          </IconButton>
        }
      />
    </>
  );
}