react-router-dom#useNavigate JavaScript Examples

The following examples show how to use react-router-dom#useNavigate. 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: UserMoreMenu.js    From course-manager with MIT License 6 votes vote down vote up
function UserMoreMenu({ id }) {
  const navigate = useNavigate();
  const ref = useRef(null);
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <IconButton ref={ref} onClick={() => setIsOpen(true)}>
        <Icon icon={moreVerticalFill} width={20} height={20} />
      </IconButton>

      <Menu
        open={isOpen}
        anchorEl={ref.current}
        onClose={() => setIsOpen(false)}
        PaperProps={{
          sx: { width: 200, maxWidth: '100%' }
        }}
        anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
        transformOrigin={{ vertical: 'top', horizontal: 'right' }}
      >
        <MenuItem sx={{ color: 'text.secondary' }} onClick={() => navigate(id)}>
          <ListItemIcon>
            <Icon icon={trash2Outline} width={24} height={24} />
          </ListItemIcon>
          <ListItemText primary="Delete" primaryTypographyProps={{ variant: 'body2' }} />
        </MenuItem>

        <MenuItem component={RouterLink} to="#" sx={{ color: 'text.secondary' }}>
          <ListItemIcon>
            <Icon icon={editFill} width={24} height={24} />
          </ListItemIcon>
          <ListItemText primary="Create" primaryTypographyProps={{ variant: 'body2' }} />
        </MenuItem>
      </Menu>
    </>
  );
}
Example #2
Source File: DogAdd.js    From citu-api with MIT License 6 votes vote down vote up
DogAdd = ({addHandler}) => {
    const [name, setName] = useState('');
    const navigate = useNavigate();
    const formHandler = (e) => {
        e.preventDefault();
        if (!name) {
            alert('Name is required!');
            return;
        }
        addHandler({id: 0, name});
        navigate('/dogs');
    }
    return (
        <form onSubmit={formHandler}>
            <label>Name</label>
            <input type="text" onChange={(e)=>{setName(e.target.value)}} />
            <button>Add</button>
        </form>
    );
}
Example #3
Source File: index.jsx    From react-sendbird-messenger with GNU General Public License v3.0 6 votes vote down vote up
export default function Dashboard() {
    let navigate = useNavigate()
    const { isMobile } = useDeviceDetect()

    const [loadingLogout, setLoadingLogout] = useState(false)

    const handleLogout = () => {
        setLoadingLogout(true)
        setTimeout(() => {
            navigate('/')

            setLoadingLogout(false)
        }, 1000)
    }

    return (
        <Fragment>
            <DashboardProvider>
                <Loading spinning={loadingLogout}>
                    {isMobile ? (
                        <Mobile handleLogout={handleLogout} />
                    ) : (
                        <Web handleLogout={handleLogout} />
                    )}
                </Loading>
            </DashboardProvider>
        </Fragment>
    )
}
Example #4
Source File: NavBar.js    From conectadev with MIT License 6 votes vote down vote up
function NavBar() {
  const classes = useStyles();
  const navigate = useNavigate();
  const account = useSelector((state) => state.account);
  const isAuthenticated = !!account.user;

  return (
    <Paper className={classes.root}>
      {!isAuthenticated && (
        <Button
          variant="outlined"
          color="secondary"
          className={classes.button}
          onClick={() => navigate('/sign-up')}
        >
          Registrar Gratis
        </Button>
      )}
      <ListSubheader>Tags em alta</ListSubheader>
      {tags.map((item) => (
        <ListItem dense button key={`item-${item.id}-${item.name}`}>
          <ListItemText primary={`#${item.name}`} />
        </ListItem>
      ))}
      <ListItem button>Exibir mais Tags</ListItem>
    </Paper>
  );
}
Example #5
Source File: Album.js    From Google-Photos-Clone with MIT License 6 votes vote down vote up
function Album({ id, data }) {
  const dispatch = useDispatch();
  const navigate = useNavigate();

  const handleAlbumClick = () => {
    dispatch(setCurrentAlbum({ albumId: id, albumName: data.name }));
    navigate(`/album/${data.name}`);
  };

  return (
    <div className="homepage__photoAlbum" onClick={handleAlbumClick}>
      <PhotoLibraryIcon fontSize="large" />
      <Typography variant="h6">{data.name}</Typography>
    </div>
  );
}
Example #6
Source File: Artist.js    From Octave with MIT License 6 votes vote down vote up
// Aritist card present inside Home Page
function Artist({ id, data }) {
  const navigate = useNavigate();

  const goToArtistPage = () => {
    navigate(`/artist/${id}`);
  };

  return (
    <div className="artist" onClick={goToArtistPage}>
      <img src={data.imageUrl} alt="" className="artist__image" />
      <p className="artist__name"> {data.name} </p>
    </div>
  );
}
Example #7
Source File: Dashboard.js    From cypress-audit with MIT License 6 votes vote down vote up
Dashboard = () => {
  const [username, setUsername] = React.useState();
  const navigate = useNavigate();

  React.useEffect(() => {
    const localUsername = window.localStorage.getItem("username");
    if (Boolean(localUsername)) {
      setUsername(localUsername);
    } else {
      navigate("/");
    }
  }, [navigate]);

  return username ? (
    <Layout>
      <div className="card">
        <h1>Dashboard</h1>
        Welcome <strong className="secondary">{username}</strong>
      </div>
    </Layout>
  ) : null;
}
Example #8
Source File: index.js    From rainbow-modules with MIT License 6 votes vote down vote up
WhenAuthenticatedV6 = (props) => {
    // eslint-disable-next-line react/prop-types
    const { component: Component, redirect, children, ...rest } = props;
    const location = useLocation();
    const navigate = useNavigate();
    const isAuth = useAuthState();

    useEffect(() => {
        if (isAuth === false) {
            navigate(redirect);
        }
    }, [isAuth, navigate, redirect]);

    if (isAuth) {
        if (Component) {
            // eslint-disable-next-line react/jsx-props-no-spreading
            return <Component location={location} {...rest} />;
        }
        return <div>{children}</div>;
    }
    return null;
}
Example #9
Source File: About.js    From React-Hooks with MIT License 6 votes vote down vote up
About = () => {
  const navigate = useNavigate();
  return (
    <div>
      <button className='btn btn-info' onClick={() => navigate(-2)}>
        Back
      </button>
    </div>
  );
}
Example #10
Source File: ToolBar.js    From js-miniapp with MIT License 6 votes vote down vote up
function withRouter(Component) {
  function ComponentWithRouterProp(props) {
    let location = useLocation();
    let navigate = useNavigate();
    let params = useParams();
    return <Component {...props} router={{ location, navigate, params }} />;
  }

  return ComponentWithRouterProp;
}
Example #11
Source File: Create.js    From web-client with Apache License 2.0 6 votes vote down vote up
ClientCreate = () => {
    const navigate = useNavigate();
    const [newClient, setNewClient] = useState(Client);

    const onFormSubmit = async (ev) => {
        ev.preventDefault();

        secureApiFetch(`/clients`, { method: 'POST', body: JSON.stringify(newClient) })
            .then(resp => {
                if (resp.status === 201) {
                    navigate(`/clients`);
                    actionCompletedToast(`The client "${newClient.name}" has been added.`);
                } else {
                    errorToast("The client could not be saved. Review the form data or check the application logs.")
                }
            })
    }

    return (
        <div>
            <div className='heading'>
                <Breadcrumb>
                    <Link to="/clients">Clients</Link>
                </Breadcrumb>
            </div>

            <Title title="New client details" icon={<IconPlus />} />

            <ClientForm onFormSubmit={onFormSubmit} client={newClient} clientSetter={setNewClient} />
        </div>
    )
}
Example #12
Source File: NotFound.jsx    From matx-react with MIT License 6 votes vote down vote up
NotFound = () => {
  const navigate = useNavigate();

  return (
    <NotFoundRoot>
      <JustifyBox>
        <IMG src="/assets/images/illustrations/404.svg" alt="" />

        <Button
          color="primary"
          variant="contained"
          sx={{ textTransform: 'capitalize' }}
          onClick={() => navigate(-1)}
        >
          Go Back
        </Button>
      </JustifyBox>
    </NotFoundRoot>
  );
}
Example #13
Source File: NotFound.jsx    From ResoBin with MIT License 6 votes vote down vote up
NotFound = () => {
  const navigate = useNavigate()
  const goHome = () => navigate('/')
  const goBack = () => navigate(-1)

  return (
    <PageContainer disable={['aside', 'menu']}>
      <Helmet>
        <title>404 Not Found</title>
        <meta name="description" content="Page not found" />
      </Helmet>

      <Box>
        <Image src={NotFoundImg} alt="404 Page not found" />

        <Subtitle>
          This page is not available.
          <br />
          <GoBack onClick={goBack}>Return back</GoBack>, or&nbsp;
          <GoBack onClick={goHome}>take me home!</GoBack>
        </Subtitle>
      </Box>
    </PageContainer>
  )
}
Example #14
Source File: App.js    From citu-api with MIT License 5 votes vote down vote up
function App() {
  const [dogs, setDogs] = useState([]);
  const [url, setUrl] = useState('/dogs/?limit=3&offset=0');
  const axiosPrivate = useAxiosPrivate();
  const navigate = useNavigate();
  const location = useLocation();

  const getDogs = async (url, options = null) => {
      setUrl(url);
      try {
          const response = await axiosPrivate.get(url, options);
          console.log(response.data);
          setDogs(response.data);
      } catch (err) {
          console.error(err);
          navigate('/login', { state: { from: location }, replace: true });
      }
  }
  useEffect(() => {
      const controller = new AbortController();
      getDogs(url, {
          signal: controller.signal
      });
      return () => {
          controller.abort();
      }
  }, []);

  const dogAddHandler = async ({name}) => {
    console.log('DOG: ', name);
    const response = await axiosPrivate.post('/dogs/', JSON.stringify({id: 0, name}));
    console.log(response.data);
    getDogs(url);
  }
  const dogUpdateHandler = async (dog) => {
    console.log('DOG: ', dog);
    const response = await axiosPrivate.put('/dogs/', JSON.stringify(dog));
    console.log(response.data);
    getDogs(url);
  }
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        {/* public routes */}
        <Route path="login" element={<Login />} />
        <Route path="register" element={<Register />} />
        <Route path="linkpage" element={<LinkPage />} />
        <Route path="unauthorized" element={<Unauthorized />} />

        {/* we want to protect these routes */}
        <Route element={<RequireAuth allowedRoles={[ROLES.User]} />}>
          <Route path="/" element={<Home />} />
        </Route>

        <Route element={<RequireAuth allowedRoles={[ROLES.Editor]} />}>
          <Route path="dogs" element={<Dogs dogs={dogs} getDogs={getDogs} />} />
          <Route path="dogs/create" element={<DogAdd addHandler={dogAddHandler} />} />
          <Route path="dogs/view/:id" element={<DogDetail />} />
          <Route path="dogs/edit/:id" element={<DogEdit updateHandler={dogUpdateHandler} />} />
        </Route>


        <Route element={<RequireAuth allowedRoles={[ROLES.Admin]} />}>
          <Route path="admin" element={<Admin />} />
        </Route>

        <Route element={<RequireAuth allowedRoles={[ROLES.Editor, ROLES.Admin]} />}>
          <Route path="lounge" element={<Lounge />} />
        </Route>

        {/* catch all */}
        <Route path="*" element={<Missing />} />
      </Route>
    </Routes>
  );
}
Example #15
Source File: Cart.js    From react-sample-projects with MIT License 5 votes vote down vote up
Cart = () => {
  const dispatch = useDispatch();
  const navigate = useNavigate();
  const cartItems = useSelector(state => state.cart.cartItems);

  const viewProductDetails = (e, item) => {
    navigate(`/product/${item.id}`);
  };

  const deleteItem = (e, item) => {
    e.stopPropagation();
    e.preventDefault();
    dispatch(deleteItemFromCart(item));
  };

  if (cartItems.length === 0) {
    return (
      <Flex>
        <Box
          m={4}
          w="100%"
          fontWeight="semibold"
          letterSpacing="wide"
          textAlign="center"
        >
          You cart empty :(
        </Box>
      </Flex>
    );
  }
  return (
    <Box m={3} p={3}>
      <Table variant="simple">
        <Thead>
          <Tr>
            <Th>#</Th>
            <Th>Image</Th>
            <Th>Title</Th>
            <Th isNumeric>Price</Th>
            <Th isNumeric>Quantity</Th>
            <Th>Action</Th>
          </Tr>
        </Thead>
        <Tbody>
          {cartItems.map((item, index) => (
            <Tr key={item.id} onClick={e => viewProductDetails(e, item)}>
              <Td>{index + 1}</Td>
              <Td>
                <Avatar size={'sm'} src={item.image} alt={item.title} />
              </Td>
              <Td>{item.title}</Td>
              <Td isNumeric>
                ${parseFloat(item.price * item.quantity).toFixed(2)}
              </Td>
              <Td isNumeric>{item.quantity}</Td>
              <Td>
                <Button onClick={e => deleteItem(e, item)}>Delete</Button>
              </Td>
            </Tr>
          ))}
        </Tbody>
      </Table>
    </Box>
  );
}
Example #16
Source File: index.js    From conectadev with MIT License 5 votes vote down vote up
function PostCard({ post }) {
  const classes = useStyles();
  const navigate = useNavigate();

  const handlePostClick = () => {
    navigate(`/post/${post.slug}`);
  };

  return (
    <Card className={classes.root} onClick={handlePostClick}>
      <CardHeader
        avatar={<Avatar src={post.author?.avatar} />}
        title={<Typography variant="h6">{post.title}</Typography>}
        subheader={
          <div className={classes.subheader}>
            <Typography variant="caption" className={classes.caption}>
              Escrito por
            </Typography>
            <Typography variant="subtitle2" className={classes.caption}>
              {post.author.name}
            </Typography>
            <Typography variant="caption" className={classes.caption}>
              {moment(post.date).fromNow()}
            </Typography>
          </div>
        }
      />
      <CardContent className={classes.content}>
        <Typography className={classes.message} variant="body1">
          {post.hashtags}
        </Typography>
        <CardActionArea>
          <img src={post.image} className={classes.image} alt="img" />
        </CardActionArea>
      </CardContent>
      <CardActions disableSpacing>
        <IconButton aria-label="like">
          <FavoriteIcon />
          <Typography
            style={{ cursor: 'pointer' }}
            color="textSecondary"
            variant="body2"
          >
            {post.likes}
          </Typography>
        </IconButton>
        <IconButton aria-label="comment">
          <MessageIcon />
          <Typography
            className={classes.reactions}
            color="textSecondary"
            variant="body2"
          >
            {post.comments}
          </Typography>
        </IconButton>
      </CardActions>
    </Card>
  );
}
Example #17
Source File: AlbumPage.js    From Google-Photos-Clone with MIT License 5 votes vote down vote up
function AlbumPage() {
  const navigate = useNavigate();
  const currentAlbum = useSelector((state) => state.currentAlbum);
  const [photos, setPhotos] = useState([]);
  const [open, setOpen] = React.useState(false);

  useEffect(() => {
    if (currentAlbum.albumId === "ROOT") navigate(`/`, { replace: true });
  }, [navigate, currentAlbum.albumId]);

  useEffect(() => {
    const unsubscribe = getAlbumPhotos(currentAlbum.albumId, (snapshot) => {
      setPhotos(
        snapshot.docs.map((doc) => ({
          id: doc.id,
          data: doc.data(),
        }))
      );
    });

    return unsubscribe;
  }, [currentAlbum.albumId]);

  const openDeleteModal = () => setOpen(true);
  const closeDeleteModal = () => setOpen(false);

  const handleDeleteAlbum = () => {
    deleteAlbum(photos, currentAlbum.albumId);
    closeDeleteModal();
    navigate(`/`, { replace: true });
  };

  return (
    <div className="albumpage">
      <div className="albumpage__header">
        <Typography variant="h5">{currentAlbum.albumName}</Typography>

        <IconButton onClick={openDeleteModal}>
          <DeleteIcon />
        </IconButton>
      </div>
      <div className="albumpage__photos">
        {photos.map(({ id, data }) => (
          <Photo key={id} id={id} data={data} />
        ))}
      </div>

      <Dialog
        open={open}
        onClose={closeDeleteModal}
        aria-labelledby="alert-dialog-title"
        aria-describedby="alert-dialog-description"
      >
        <DialogTitle id="alert-dialog-title">
          Album Delete Confirmation
        </DialogTitle>
        <DialogContent>
          <DialogContentText id="alert-dialog-description">
            Deleting the Album will also delete the Photos inside it... Do you
            want to delete this Album ?
          </DialogContentText>
        </DialogContent>
        <DialogActions>
          <Button onClick={closeDeleteModal} color="primary">
            Cancel
          </Button>
          <Button
            onClick={handleDeleteAlbum}
            color="primary"
            autoFocus
            variant="contained"
          >
            delete
          </Button>
        </DialogActions>
      </Dialog>
    </div>
  );
}
Example #18
Source File: EditVideoButtons.js    From serverless-media-portal with MIT License 5 votes vote down vote up
export default function EditVideoButtons({ isUserAnAdmin }) {
	const [displayEditButtons, setDisplayEditButtons] = useState(false);
	const [isEditModalOpen, setIsEditModalOpen] = useState(false);
	const { video, setVideo } = useContext(VideoContext);
	const { addToast } = useToasts();
	const navigate = useNavigate();

	useEffect(() => {
		setDisplayEditButtons(isUserAnAdmin);
	}, [isUserAnAdmin]);

	const onDeleteClicked = async () => {
		const res = await authGet(`http://localhost:3001/dev/deleteVideo?videoHash=${video.VideoHash}`);

		if (res && res.success) {
			addNotification("Video deleted", "success");
			navigate("/");
		} else {
			addNotification("Error deleting video", "error");
		}
	};

	const onEditFormSubmitted = async (newVideo) => {
		const res = await authPost("http://localhost:3001/dev/editVideo", {
			video: newVideo
		});

		if (res.success) {
			setVideo(newVideo);
			addNotification("Video successfully updated", "success");
		} else {
			addNotification("Error updating video", "error");
			console.error(res.message);
		}
	};

	const addNotification = (msg, type) => {
		addToast(msg, {
			appearance: type,
			autoDismiss: true
		});
	};

	return (
		!displayEditButtons || (
			<>
				<div className="text-nowrap">
					<Button
						variant="warning"
						size="sm"
						className="mr-1"
						onClick={() => setIsEditModalOpen(true)}
					>
						Edit
					</Button>

					<Button
						variant="danger"
						size="sm"
						onClick={() => {
							if (window.confirm("Are you sure you want to delete this video?")) {
								onDeleteClicked();
							}
						}}
					>
						Delete
					</Button>
				</div>

				<EditVideoModal
					video={video}
					isOpen={isEditModalOpen}
					setIsOpen={setIsEditModalOpen}
					onEditFormSubmitted={onEditFormSubmitted}
				/>
			</>
		)
	);
}
Example #19
Source File: RaceSelect.js    From 4IZ268-2021-2022-ZS with MIT License 5 votes vote down vote up
RaceSelect = () => {

    const setRace = useRace((state) => state.setCurrentRace)

    const query = useQuery(['competitions'], getCompetitions)
    const [data, setData] = useState([])
    const [page, setPage] = useState(1)

    const height = window.innerHeight - 64 - 82 - 60

    const navigate = useNavigate()

    useEffect(() => {
        if (query.data?.competitions) {
            setData(query.data.competitions.slice(0, 100))
        }
    }, [query.data])

    useEffect(() => {
        if (query.data?.competitions) {
            setData(query.data?.competitions.slice(0, page * 100))
        }
    }, [page, query.data])

    const appendData = () => {
        setPage(page + 1)
    }

    const onScroll = e => {
        if (e.target.scrollHeight - e.target.scrollTop < height + 5) {
            appendData()
        }
    }

    const onClick = (race) => {
        setRace(race.id)
        navigate('/~bukp00/sp2/' + race.id)
    }

    return (
        <>
            {
                query.data?.competitions ?
                    <div className='select-list'>
                        <List>
                            <VirtualList
                                key='list'
                                data={data}
                                height={height}
                                itemHeight={47}
                                itemKey='id'
                                onScroll={onScroll}
                            >
                                {item => (
                                    <List.Item key={item.id} onClick={() => onClick(item)}>
                                        <List.Item.Meta
                                            title={<span>{item.name}</span>}
                                            description={item.organizer + ', ' + item.date}
                                        />
                                    </List.Item>
                                )}
                            </VirtualList>
                        </List>
                    </div> :
                    <div className='select-list-loading'>
                        <Skeleton />
                    </div>
            }
        </>
    )
}
Example #20
Source File: nav.jsx    From secure-electron-template with MIT License 5 votes vote down vote up
function WithNavigate(props){
  const navigate = useNavigate();
  return <Nav {...props} navigate={navigate} />
}
Example #21
Source File: List.js    From web-client with Apache License 2.0 5 votes vote down vote up
AuditLogList = () => {
    const navigate = useNavigate();
    const query = useQuery();
    let pageNumber = query.get('page');
    pageNumber = pageNumber !== null ? parseInt(pageNumber) : 1;
    const apiPageNumber = pageNumber - 1;

    const [auditLog, setAuditLog] = useState([]);
    const [numberPages, setNumberPages] = useState(1);

    const onPageChange = pageNumber => {
        navigate(`/auditlog?page=${pageNumber + 1}`);
    }

    const reloadData = useCallback(() => {
        secureApiFetch(`/auditlog?page=${apiPageNumber}`, { method: 'GET' })
            .then(resp => {
                if (resp.headers.has('X-Page-Count')) {
                    setNumberPages(resp.headers.get('X-Page-Count'))
                }
                return resp.json()
            })
            .then((data) => {
                setAuditLog(data);
            })
    }, [apiPageNumber]);

    useEffect(() => {
        reloadData()
    }, [reloadData])

    const onExportClick = () => {
        downloadFromApi('/system/data?entities=auditlog');
    }

    return <>
        <PageTitle value={`Audit log - Page ${pageNumber}`} />
        <div className='heading'>
            <Breadcrumb>
                <div>System</div>
            </Breadcrumb>
            <PaginationV2 page={apiPageNumber} total={numberPages} onPageChange={onPageChange} />
            <ExportButton onClick={onExportClick} />
        </div>
        <Title type="System" title='Audit Log' icon={<IconEye />} />
        <AuditLogsTable auditLog={auditLog} />
    </>
}
Example #22
Source File: Homepage.jsx    From CRM with Apache License 2.0 5 votes vote down vote up
Homepage = () => {
  const matches = useMediaQuery("(min-width:600px)");

  const navigate = useNavigate();

  return (
    <section className="wrapper" style={{ borderTop: "3px solid #1d8cf8" }}>
      <div className="row p-4">
        <div className="col-md-6">
          <h1
            style={{
              fontSize: matches ? "7rem" : "3rem",
              textTransform: "uppercase",
              textAlign: "justify",
              fontWeight: 700,
              textShadow: "-0.04em 0.04em #0f3460, -0.06em 0.06em #fd5d93",
              color: "#fff",
            }}
          >
            Easy CRM for Your Basic CRM needs
          </h1>
          <p className="text-danger lead title">*It's in development</p>
        </div>
        <div className="col-md-6">
          {matches && <img src={SomePic} alt="" style={{ maxWidth: "100%" }} />}
          <div>
            <Button variant="contained" onClick={() => navigate("/login")}>
              Login
            </Button>
            <Button
              variant="outlined"
              color="secondary"
              className="ms-3"
              onClick={() => navigate("/signup")}
            >
              Get started
            </Button>
          </div>
        </div>
      </div>
    </section>
  );
}
Example #23
Source File: SideNav.jsx    From Socialgram with Apache License 2.0 5 votes vote down vote up
Modal = () => {
  const navigate = useNavigate();

  const logout = () => {
    localStorage.clear();
    navigate("/");
  };
  return (
    <div
      className="modal fade"
      id="exampleModal"
      tabIndex="-1"
      aria-labelledby="exampleModalLabel"
      aria-hidden="true"
    >
      <div className="modal-dialog modal-dialog-centered">
        <div className="modal-content modal-dark">
          <div className="modal-header border border-0">
            <h4 className="modal-title">Important !!</h4>
            <div data-bs-dismiss="modal" style={{ cursor: "pointer" }}>
              close
            </div>
          </div>

          <div className="modal-body">You sure want to logout ?</div>

          <div className="modal-footer border border-0">
            <button
              type="button"
              className="btn btn-primary"
              data-bs-dismiss="modal"
              onClick={logout}
            >
              Logout
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}
Example #24
Source File: ForgotPassword.jsx    From matx-react with MIT License 5 votes vote down vote up
ForgotPassword = () => {
  const navigate = useNavigate();
  const [email, setEmail] = useState('[email protected]');

  const handleFormSubmit = () => {
    console.log(email);
  };

  return (
    <ForgotPasswordRoot>
      <Card className="card">
        <Grid container>
          <Grid item xs={12}>
            <JustifyBox p={4}>
              <img width="300" src="/assets/images/illustrations/dreamer.svg" alt="" />
            </JustifyBox>

            <ContentBox>
              <form onSubmit={handleFormSubmit}>
                <TextField
                  type="email"
                  name="email"
                  size="small"
                  label="Email"
                  value={email}
                  variant="outlined"
                  onChange={(e) => setEmail(e.target.value)}
                  sx={{ mb: 3, width: '100%' }}
                />

                <Button fullWidth variant="contained" color="primary" type="submit">
                  Reset Password
                </Button>

                <Button
                  fullWidth
                  color="primary"
                  variant="outlined"
                  onClick={() => navigate(-1)}
                  sx={{ mt: 2 }}
                >
                  Go Back
                </Button>
              </form>
            </ContentBox>
          </Grid>
        </Grid>
      </Card>
    </ForgotPasswordRoot>
  );
}
Example #25
Source File: CourseResourceContainer.jsx    From ResoBin with MIT License 5 votes vote down vote up
CourseResourceContainer = () => {
  const { code } = useParams()
  const navigate = useNavigate()
  const { isMobileS } = useResponsive()

  const [resources, setResources] = useState([])
  const [loading, setLoading] = useState(false)

  useEffect(() => {
    const fetchResources = async () => {
      try {
        setLoading(true)
        const response = await API.courses.listResources({ code })
        setResources(response)
      } catch (error) {
        toast({ status: 'error', content: error })
      } finally {
        setLoading(false)
      }
    }

    fetchResources()
  }, [code])

  const redirectContribute = () => navigate(`/contribute?course=${code}`)

  if (loading) return <LoaderAnimation />

  return (
    <>
      <Header>
        <h1 style={{ fontSize: '1.25rem' }}>Resources</h1>

        <ButtonContainer>
          {isMobileS ? (
            <CourseContentRequestIcon
              code={code}
              type="resources"
              style={{ marginRight: '0.75rem' }}
            />
          ) : (
            <CourseContentRequest
              code={code}
              type="resources"
              style={{ marginRight: '0.75rem' }}
            />
          )}

          <ButtonSquare
            type="primary"
            onClick={redirectContribute}
            icon={<CloudUpload size="16" />}
          >
            Upload
          </ButtonSquare>
        </ButtonContainer>
      </Header>

      {resources.length ? (
        <CourseResourceGrid items={resources} />
      ) : (
        <span style={{ fontSize: '0.875rem' }}>No resources found</span>
      )}
    </>
  )
}
Example #26
Source File: LoginForm.js    From course-manager with MIT License 4 votes vote down vote up
// ----------------------------------------------------------------------

export default function LoginForm() {
  const navigate = useNavigate();
  const [showPassword, setShowPassword] = useState(false);

  const LoginSchema = Yup.object().shape({
    username: Yup.string().required('Username is required'),
    password: Yup.string().required('Password is required')
  });

  setUpdateLoginState((newProfile) => {
    console.log(`${newProfile.username} logged in`);
  });

  const formik = useFormik({
    initialValues: {
      username: '',
      password: ''
    },
    validationSchema: LoginSchema,
    onSubmit: async (values) => {
      const user = await apis.auth.login(values);
      if (user) navigate('/dashboard', { replace: true });
    }
  });

  const { errors, touched, isSubmitting, handleSubmit, getFieldProps } = formik;

  const handleShowPassword = () => {
    setShowPassword((show) => !show);
  };

  return (
    <FormikProvider value={formik}>
      <Form autoComplete="off" noValidate onSubmit={handleSubmit}>
        <Stack spacing={3}>
          <TextField
            fullWidth
            autoComplete="username"
            type="text"
            label="Username"
            {...getFieldProps('username')}
            error={Boolean(touched.username && errors.username)}
            helperText={touched.username && errors.username}
          />

          <TextField
            fullWidth
            autoComplete="current-password"
            type={showPassword ? 'text' : 'password'}
            label="Password"
            {...getFieldProps('password')}
            InputProps={{
              endAdornment: (
                <InputAdornment position="end">
                  <IconButton onClick={handleShowPassword} edge="end">
                    <Icon icon={showPassword ? eyeFill : eyeOffFill} />
                  </IconButton>
                </InputAdornment>
              )
            }}
            error={Boolean(touched.password && errors.password)}
            helperText={touched.password && errors.password}
          />
        </Stack>

        <Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ my: 2 }}>
          <Link component={RouterLink} variant="subtitle2" to="#">
            Forgot password?
          </Link>
        </Stack>

        <LoadingButton
          fullWidth
          size="large"
          type="submit"
          variant="contained"
          loading={isSubmitting}
        >
          Login
        </LoadingButton>
      </Form>
    </FormikProvider>
  );
}
Example #27
Source File: index.js    From plenty-interface with GNU General Public License v3.0 4 votes vote down vote up
LiquidityNew = (props) => {
  const { activeTab, tokenIn, setTokenIn, tokenOut, setTokenOut, setActiveTab } =
    useLocationStateInLiquidity();

  const [searchQuery, setSearchQuery] = useState('');
  const [swapData, setSwapData] = useState({});
  const [tokenType, setTokenType] = useState('tokenIn');
  const [show, setShow] = useState(false);
  const [slippage, setSlippage] = useState(0.5);
  const [recepient, setRecepient] = useState('');
  const [tokenContractInstances, setTokenContractInstances] = useState({});
  const [showConfirmAddSupply, setShowConfirmAddSupply] = useState(false);
  const [showConfirmRemoveSupply, setShowConfirmRemoveSupply] = useState(false);
  const [showConfirmTransaction, setShowConfirmTransaction] = useState(false);
  const [loaderMessage, setLoaderMessage] = useState({});
  const [loaderInButton, setLoaderInButton] = useState(false);
  const [loading, setLoading] = useState(false);
  const [getTokenPrice, setGetTokenPrice] = useState({});
  const [userBalances, setUserBalances] = useState({});

  const location = useLocation();
  const navigate = useNavigate();
  const { pathname } = location;
  const splitLocation = pathname.split('/');
  const [searchParams] = useSearchParams();
  const [isLiquidityPosition, setLiquidityPosition] = useState(false);
  const [positionDetails, setPositionDetails] = useState({});
  const [isPositionAvailable, setPositionAvailable] = useState(false);

  const [balanceUpdate, setBalanceUpdate] = useState(false);

  useEffect(async () => {
    const isStable = isTokenPairStable(tokenIn.name, tokenOut.name);

    const ress = await getLpTokenBalanceForPair(tokenIn.name, tokenOut.name, props.walletAddress);

    setPositionAvailable(ress.isLiquidityAvailable);
    if (ress.isLiquidityAvailable) {
      let res;
      if (isStable) {
        if (CONFIG.AMM[CONFIG.NETWORK][tokenIn.name].DEX_PAIRS[tokenOut.name]?.type === 'xtz') {
          res = await getLiquidityPositionDetailsStable(
            tokenIn.name,
            tokenOut.name,
            props.walletAddress,
          );
        } else if (
          CONFIG.AMM[CONFIG.NETWORK][tokenIn.name].DEX_PAIRS[tokenOut.name]?.type === 'veStableAMM'
        ) {
          res = await getLiquidityPositionDetails(tokenIn.name, tokenOut.name, props.walletAddress);
        }
      } else {
        res = await getLiquidityPositionDetails(tokenIn.name, tokenOut.name, props.walletAddress);
      }

      setPositionDetails(res);
    }
  }, [tokenIn, tokenOut, props]);

  useEffect(() => {
    setLoaderInButton(true);

    getTokenPrices().then((tokenPrice) => {
      setGetTokenPrice(tokenPrice);
    });
  }, []);

  const activeKey = useMemo(() => {
    if (location.pathname === '/liquidity/remove') {
      return 'remove';
    }

    return 'add';
  }, [location.pathname]);
  useEffect(() => {
    if (!location.pathname.includes('liquidityPositions')) {
      setLiquidityPosition(false);
    }
  }, [searchParams]);

  const handleClose = () => {
    setShow(false);

    setShowConfirmAddSupply(false);
    setShowConfirmRemoveSupply(false);
    setShowConfirmTransaction(false);
    setSearchQuery('');
  };

  useEffect(() => {
    const updateBalance = async () => {
      if (props.walletAddress) {
        setTokenContractInstances({});

        const tzBTCName = 'tzBTC';
        const balancePromises = [];

        tokenIn.name === tzBTCName
          ? balancePromises.push(fetchtzBTCBalance(props.walletAddress))
          : balancePromises.push(
              config.AMM[config.NETWORK][tokenIn.name]?.DEX_PAIRS[tokenOut.name]?.type === 'xtz'
                ? getUserBalanceByRpcStable(tokenIn.name, props.walletAddress)
                : getUserBalanceByRpc(tokenIn.name, props.walletAddress),
            );

        tokenOut.name === tzBTCName
          ? balancePromises.push(fetchtzBTCBalance(props.walletAddress))
          : balancePromises.push(
              config.AMM[config.NETWORK][tokenIn.name]?.DEX_PAIRS[tokenOut.name]?.type === 'xtz'
                ? getUserBalanceByRpcStable(tokenOut.name, props.walletAddress)
                : getUserBalanceByRpc(tokenOut.name, props.walletAddress),
            );

        if (
          config.AMM[config.NETWORK][tokenIn.name]?.DEX_PAIRS[tokenOut.name]?.type === 'xtz'
            ? config.STABLESWAP[config.NETWORK][tokenIn.name].DEX_PAIRS[tokenOut.name]
            : config.AMM[config.NETWORK][tokenIn.name].DEX_PAIRS[tokenOut.name]
        ) {
          const lpToken = isTokenPairStable(tokenIn.name, tokenOut.name)
            ? config.STABLESWAP[config.NETWORK][tokenIn.name].DEX_PAIRS[tokenOut.name]
                .liquidityToken
            : config.AMM[config.NETWORK][tokenIn.name].DEX_PAIRS[tokenOut.name].liquidityToken;

          balancePromises.push(
            config.AMM[config.NETWORK][tokenIn.name]?.DEX_PAIRS[tokenOut.name]?.type === 'xtz'
              ? getUserBalanceByRpcStable(lpToken, props.walletAddress)
              : getUserBalanceByRpc(lpToken, props.walletAddress),
          );
        }
        const balanceResponse = await Promise.all(balancePromises);

        setUserBalances((prev) => ({
          ...prev,
          ...balanceResponse.reduce(
            (acc, cur) => ({
              ...acc,
              [cur.identifier]: cur.balance,
            }),
            {},
          ),
        }));
      }
    };
    updateBalance();
  }, [tokenIn, tokenOut, props, balanceUpdate]);

  const selectToken = (token) => {
    setLoaderInButton(true);

    setSwapData({});

    if (tokenType === 'tokenIn') {
      setTokenIn({
        name: token.name,
        image: token.image,
      });

      if (token.name === 'tez') {
        setTokenOut({
          name: 'ctez',
          image: ctez,
        });
      } else if (token.name === 'EURL') {
        setTokenOut({
          name: 'agEUR.e',
          image: ageure,
        });
      } else if (token.name === 'agEUR.e') {
        setTokenOut({
          name: 'EURL',
          image: eurl,
        });
      }
    } else {
      setTokenOut({
        name: token.name,
        image: token.image,
      });
    }
    handleClose();
  };

  useEffect(() => {
    if (activeTab === 'liquidity') {
      if (
        Object.prototype.hasOwnProperty.call(tokenIn, 'name') &&
        Object.prototype.hasOwnProperty.call(tokenOut, 'name')
      ) {
        const pairExists = isTokenPairStable(tokenIn.name, tokenOut.name)
          ? !!config.STABLESWAP[config.NETWORK][tokenIn.name].DEX_PAIRS[tokenOut.name]
          : !!config.AMM[config.NETWORK][tokenIn.name].DEX_PAIRS[tokenOut.name];

        if (pairExists) {
          if (config.AMM[config.NETWORK][tokenIn.name]?.DEX_PAIRS[tokenOut.name]?.type === 'xtz') {
            loadSwapDataStable(tokenIn.name, tokenOut.name).then((data) => {
              if (data.success) {
                setSwapData(data);

                setLoaderInButton(false);
              }
            });
          } else if (
            config.AMM[config.NETWORK][tokenIn.name]?.DEX_PAIRS[tokenOut.name]?.type ===
            'veStableAMM'
          ) {
            loadSwapDataGeneralStable(tokenIn.name, tokenOut.name).then((data) => {
              if (data.success) {
                setSwapData(data);

                setLoaderInButton(false);
              }
            });
          } else {
            loadSwapData(tokenIn.name, tokenOut.name).then((data) => {
              if (data.success) {
                setSwapData(data);

                setLoaderInButton(false);
              }
            });
          }
        }
      }
    }
  }, [tokenIn, tokenOut, activeTab, splitLocation[1]]);

  const handleTokenType = (type) => {
    setBalanceUpdate(false);
    setShow(true);
    setTokenType(type);
    setLoading(false);
  };

  const fetchUserWalletBalance = () => {
    setLoaderInButton(true);
  };

  const handleLoaderMessage = (type, message) => {
    setLoaderMessage({
      type: type,
      message: message,
    });
    setLoading(false);
  };

  const resetAllValues = () => {
    setRecepient('');
    setTokenType('tokenIn');
  };

  const changeLiquidityType = (tab) => {
    const tokenAFromParam = searchParams.get('tokenA');
    const tokenBFromParam = searchParams.get('tokenB');
    navigate({
      pathname: `/liquidity/${tab}`,
      search: `?${createSearchParams({
        ...(tokenAFromParam ? { tokenA: tokenAFromParam } : {}),
        ...(tokenBFromParam ? { tokenB: tokenBFromParam } : {}),
      })}`,
    });
  };
  const redirectLiquidityPositions = (value) => {
    setLiquidityPosition(value);

    value ? setActiveTab('liquidityPositions') : setActiveTab('liquidity');
  };

  useEffect(() => {
    const tokenAFromParam = searchParams.get('tokenA');
    const tokenBFromParam = searchParams.get('tokenB');

    if (tokenAFromParam !== tokenBFromParam) {
      if (tokenAFromParam) {
        liquidityTokens.map((token) => {
          if (token.name === tokenAFromParam) {
            setTokenIn({
              name: tokenAFromParam,
              image: token.image,
            });
          }
        });
      }

      if (tokenBFromParam) {
        liquidityTokens.map((token) => {
          if (token.name === tokenBFromParam) {
            setTokenOut({
              name: tokenBFromParam,
              image: token.image,
            });
          }
        });
      }
    }
  }, [searchParams]);

  return (
    <Container fluid className="removing-padding">
      {props.walletAddress && (
        <p
          className="redirect-label-lp"
          style={{ cursor: 'pointer' }}
          onClick={() => redirectLiquidityPositions(!isLiquidityPosition)}
        >
          {isLiquidityPosition && (
            <span className={clsx('material-icons', 'arrow-forward', 'mt-1', 'ml-0')}>
              arrow_back_ios_icon
            </span>
          )}
          {isLiquidityPosition ? 'Back' : 'View Liquidity Positions'}
          {!isLiquidityPosition && (
            <span className={clsx('material-icons', 'arrow-forward', 'mt-1')}>
              arrow_forward_ios_icon
            </span>
          )}
        </p>
      )}
      {isLiquidityPosition && <div className="liq-label">Position overview</div>}

      {!isLiquidityPosition ? (
        <Col
          sm={8}
          md={6}
          className={clsx('liquidity-content-container', !props.walletAddress && 'liq-margin')}
        >
          <div className="">
            <Tabs
              activeKey={activeKey}
              className="liq-container-tab"
              onSelect={(e) => changeLiquidityType(e)}
              mountOnEnter={true}
              unmountOnExit={true}
            >
              <Tab eventKey="add" title="Add">
                <AddLiquidity
                  walletAddress={props.walletAddress}
                  connecthWallet={props.connecthWallet}
                  tokenIn={tokenIn}
                  tokenOut={tokenOut}
                  handleTokenType={handleTokenType}
                  swapData={swapData}
                  userBalances={userBalances}
                  tokenContractInstances={tokenContractInstances}
                  getTokenPrice={getTokenPrice}
                  setSlippage={setSlippage}
                  setRecepient={setRecepient}
                  recepient={recepient}
                  slippage={slippage}
                  loading={loading}
                  setLoading={setLoading}
                  handleLoaderMessage={handleLoaderMessage}
                  loaderMessage={loaderMessage}
                  handleClose={handleClose}
                  showConfirmAddSupply={showConfirmAddSupply}
                  setShowConfirmAddSupply={setShowConfirmAddSupply}
                  showConfirmRemoveSupply={showConfirmRemoveSupply}
                  setShowConfirmRemoveSupply={setShowConfirmRemoveSupply}
                  setLoaderMessage={setLoaderMessage}
                  resetAllValues={resetAllValues}
                  fetchUserWalletBalance={fetchUserWalletBalance}
                  setTokenIn={setTokenIn}
                  setTokenOut={setTokenOut}
                  tokens={liquidityTokens}
                  loaderInButton={loaderInButton}
                  setLoaderInButton={setLoaderInButton}
                  setShowConfirmTransaction={setShowConfirmTransaction}
                  showConfirmTransaction={showConfirmTransaction}
                  positionDetails={positionDetails}
                  setPositionAvailable={setPositionAvailable}
                  isPositionAvailable={isPositionAvailable}
                  setPositionDetails={setPositionDetails}
                  theme={props.theme}
                  setBalanceUpdate={setBalanceUpdate}
                  balanceUpdate={balanceUpdate}
                  {...props}
                />
              </Tab>
              {/* {isPositionAvailable ? ( */}
              <Tab eventKey="remove" title="Remove">
                <RemoveLiquidity
                  theme={props.theme}
                  walletAddress={props.walletAddress}
                  connecthWallet={props.connecthWallet}
                  tokenIn={tokenIn}
                  tokenOut={tokenOut}
                  handleTokenType={handleTokenType}
                  swapData={swapData}
                  userBalances={userBalances}
                  tokenContractInstances={tokenContractInstances}
                  getTokenPrice={getTokenPrice}
                  setSlippage={setSlippage}
                  setRecepient={setRecepient}
                  recepient={recepient}
                  slippage={slippage}
                  loading={loading}
                  setLoading={setLoading}
                  handleLoaderMessage={handleLoaderMessage}
                  loaderMessage={loaderMessage}
                  handleClose={handleClose}
                  showConfirmAddSupply={showConfirmAddSupply}
                  setShowConfirmAddSupply={setShowConfirmAddSupply}
                  showConfirmRemoveSupply={showConfirmRemoveSupply}
                  setShowConfirmRemoveSupply={setShowConfirmRemoveSupply}
                  setLoaderMessage={setLoaderMessage}
                  resetAllValues={resetAllValues}
                  fetchUserWalletBalance={fetchUserWalletBalance}
                  setTokenIn={setTokenIn}
                  setTokenOut={setTokenOut}
                  tokens={liquidityTokens}
                  loaderInButton={loaderInButton}
                  setLoaderInButton={setLoaderInButton}
                  isStableSwap={isTokenPairStable(tokenIn.name, tokenOut.name)}
                  setShowConfirmTransaction={setShowConfirmTransaction}
                  showConfirmTransaction={showConfirmTransaction}
                  positionDetails={positionDetails}
                  setPositionAvailable={setPositionAvailable}
                  isPositionAvailable={isPositionAvailable}
                  setPositionDetails={setPositionDetails}
                />
              </Tab>
              {/* ) : null} */}
            </Tabs>
            <div className="settings-liq">
              <SettingsLiq
                slippage={slippage}
                setSlippage={setSlippage}
                walletAddress={props.walletAddress}
                theme={props.theme}
              />
            </div>
          </div>
        </Col>
      ) : (
        <LiquidityPositions walletAddress={props.walletAddress} theme={props.theme} />
      )}
      <LiquidityModal
        show={show}
        activeTab={activeTab}
        onHide={handleClose}
        selectToken={selectToken}
        tokens={liquidityTokens}
        tokenIn={tokenIn}
        tokenOut={tokenOut}
        tokenType={tokenType}
        searchQuery={searchQuery}
        setSearchQuery={setSearchQuery}
      />
    </Container>
  );
}
Example #28
Source File: Login.js    From citu-api with MIT License 4 votes vote down vote up
Login = () => {
    const { auth, setAuth, keepLogin } = useAuth();
    const navigate = useNavigate();
    const location = useLocation();
    const from = location.state?.from?.pathname || "/";

    const userRef = useRef();
    const errRef = useRef();

    const [user, setUser] = useState('');
    const [pwd, setPwd] = useState('');
    const [errMsg, setErrMsg] = useState('');

    useEffect(() => {
        if (auth?.accessToken) {
            navigate(from, { replace: true });
        }
        userRef.current.focus();
    }, [])

    useEffect(() => {
        setErrMsg('');
    }, [user, pwd])

    const handleSubmit = async (e) => {
        e.preventDefault();

        try {
            const response = await axios.post(LOGIN_URL,
                JSON.stringify({
                    username: user,
                    password: pwd
                }),
                {
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    withCredentials: true
                }
            );
            console.log(typeof(response), response);
            const accessToken = response?.data?.token;
            if (!accessToken) {
                throw new Error('Access token not found.');
            }
            const userLogin = { user: 2001, pwd, accessToken };
            setAuth(userLogin);
            keepLogin(userLogin);
            setUser('');
            setPwd('');
            console.log(from, accessToken);
            navigate(from, { replace: true });
        } catch (err) {
            if (!err?.response) {
                setErrMsg('No Server Response');
            } else if (err.response?.status === 400) {
                setErrMsg('Missing Username or Password');
            } else if (err.response?.status === 403) {
                setErrMsg('Unauthorized');
            } else {
                setErrMsg('Login Failed');
            }
            errRef.current.focus();
        }
    }

    return (

        <section>
            <p ref={errRef} className={errMsg ? "errmsg" : "offscreen"} aria-live="assertive">{errMsg}</p>
            <h1>Sign In</h1>
            <form onSubmit={handleSubmit}>
                <label htmlFor="username">Username:</label>
                <input
                    type="text"
                    id="username"
                    ref={userRef}
                    autoComplete="off"
                    onChange={(e) => setUser(e.target.value)}
                    value={user}
                    required
                />

                <label htmlFor="password">Password:</label>
                <input
                    type="password"
                    id="password"
                    onChange={(e) => setPwd(e.target.value)}
                    value={pwd}
                    required
                />
                <button>Sign In</button>
            </form>
            <p>
                Need an Account?<br />
                <span className="line">
                    <Link to="/register">Sign Up</Link>
                </span>
            </p>
        </section>

    )
}
Example #29
Source File: App.jsx    From amazon-ivs-feed-web-demo with MIT No Attribution 4 votes vote down vote up
App = () => {
  const { isMobileView } = useMobileBreakpoint();
  const { activeStream, setStreams } = useStream();

  const [metadataVisible, setMetadataVisible] = useState(true);
  const metadataRef = useRef();
  const params = useParams();
  const navigate = useNavigate();

  useEffect(() => {
    const fetchStreams = async () => {
      try {
        const response = await fetch(feedJSON);
        if (response.ok) {
          const { streams } = await response.json();
          const paramsStreamId = parseInt(params.id);
          const streamIds = streams.map((s) => s.id);
          const initialStreamId = streamIds.includes(paramsStreamId) ? paramsStreamId : 0;
          setStreams(streams, initialStreamId);
        } else throw new Error(response.statusText);
      } catch (e) {
        console.error(e);
      }
    };

    fetchStreams();
  }, [setStreams]); // eslint-disable-line react-hooks/exhaustive-deps

  // Update the page URL for the active stream
  useEffect(() => {
    if (!activeStream) return;

    const activeStreamId = activeStream.data.id;
    if (activeStreamId !== params.id) {
      navigate(`/${activeStreamId}`, { replace: true });
    }
  }, [activeStream, params.id, navigate]);

  useEffect(() => toggleMetadata(!isMobileView, false), [isMobileView]); // eslint-disable-line react-hooks/exhaustive-deps

  const toggleMetadata = useCallback(
    (show = !metadataVisible, transition = true) => {
      if (metadataRef.current) {
        const { scrollHeight: contentHeight, style } = metadataRef.current;
        style.transition = transition ? 'height 0.2s ease-out' : '';

        if (show) {
          // Show metadata
          style.height = isMobileView ? `${contentHeight}px` : '100%';
          metadataRef.current.addEventListener(
            'transitionend',
            () => (style.height = null),
            { once: true }
          );
        } else {
          // Hide metadata
          if (transition) {
            requestAnimationFrame(() => {
              style.height = `${contentHeight}px`;
              requestAnimationFrame(() => (style.height = '0'));
            });
          } else style.height = '0';
        }

        setMetadataVisible(show);
      }
    },
    [metadataVisible, isMobileView]
  );

  return (
    <div className="grid">
      <div className="feed">
        <Feed toggleMetadata={toggleMetadata} metadataVisible={metadataVisible} />
      </div>
      <div className="metadata" ref={metadataRef}>
        <StreamMetadata toggleMetadata={toggleMetadata} />
      </div>
    </div>
  );
}