From 6dd32780a7c6717afd9d2f4a995aa4880f118e43 Mon Sep 17 00:00:00 2001 From: sahil-tgs Date: Tue, 16 Jul 2024 00:03:28 +0530 Subject: [PATCH 01/13] NavBar update --- frontend/src/basic/NavBar/NavBar.tsx | 195 +++++++++++---------------- 1 file changed, 82 insertions(+), 113 deletions(-) diff --git a/frontend/src/basic/NavBar/NavBar.tsx b/frontend/src/basic/NavBar/NavBar.tsx index 9cabcfd03..c153bf313 100644 --- a/frontend/src/basic/NavBar/NavBar.tsx +++ b/frontend/src/basic/NavBar/NavBar.tsx @@ -1,44 +1,55 @@ import React, { useContext, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useLocation, useNavigate } from 'react-router-dom'; -import { Tabs, Tab, Paper, useTheme } from '@mui/material'; -import MoreTooltip from './MoreTooltip'; - -import { type Page, isPage } from '.'; - import { - SettingsApplications, - SmartToy, - Storefront, - AddBox, - Assignment, - MoreHoriz, -} from '@mui/icons-material'; -import RobotAvatar from '../../components/RobotAvatar'; + BottomNavigation, + BottomNavigationAction, + Paper, + styled, + useTheme, + useMediaQuery, +} from '@mui/material'; +import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; +import StorefrontOutlinedIcon from '@mui/icons-material/StorefrontOutlined'; +import AddBoxOutlinedIcon from '@mui/icons-material/AddBoxOutlined'; +import AssignmentOutlinedIcon from '@mui/icons-material/AssignmentOutlined'; +import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined'; + import { AppContext, type UseAppStoreType, closeAll } from '../../contexts/AppContext'; import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext'; +import { type Page, isPage } from '.'; + +const StyledBottomNavigation = styled(BottomNavigation)(({ theme }) => ({ + width: '100%', + maxWidth: '100%', + padding: theme.spacing(0.5, 0), + borderRadius: 'inherit', +})); + +const StyledBottomNavigationAction = styled(BottomNavigationAction)(({ theme }) => ({ + minWidth: 'auto', + padding: theme.spacing(0.5, 0), + '& .MuiBottomNavigationAction-label': { + fontSize: '0.75rem', + }, + '& .MuiSvgIcon-root': { + fontSize: '1.5rem', + }, +})); const NavBar = (): JSX.Element => { - const theme = useTheme(); const { t } = useTranslation(); - const { page, setPage, settings, setSlideDirection, open, setOpen, windowSize, navbarHeight } = + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + const { page, setPage, setSlideDirection, open, setOpen } = useContext(AppContext); - const { garage, robotUpdatedAt } = useContext(GarageContext); + const { garage } = useContext(GarageContext); const { setCurrentOrderId } = useContext(FederationContext); const navigate = useNavigate(); const location = useLocation(); - const smallBar = windowSize?.width < 50; - const color = settings.network === 'mainnet' ? 'primary' : 'secondary'; - - const tabSx = smallBar - ? { - position: 'relative', - bottom: garage.getSlot()?.hashId ? '0.9em' : '0.13em', - minWidth: '1em', - } - : { position: 'relative', bottom: '1em', minWidth: '2em' }; + const [value, setValue] = React.useState(page); const pagesPosition = { robot: 1, @@ -49,11 +60,6 @@ const NavBar = (): JSX.Element => { }; useEffect(() => { - // re-render on orde rand robot updated at for latest orderId in tab - }, [robotUpdatedAt]); - - useEffect(() => { - // change tab (page) into the current route const pathPage: Page | string = location.pathname.split('/')[1]; if (pathPage === 'index.html') { navigate('/robot'); @@ -62,9 +68,10 @@ const NavBar = (): JSX.Element => { if (isPage(pathPage)) { setPage(pathPage); } - }, [location]); + setValue(pathPage as Page); + }, [location, navigate, setPage]); - const handleSlideDirection = function (oldPage: Page, newPage: Page): void { + const handleSlideDirection = (oldPage: Page, newPage: Page): void => { const oldPos: number = pagesPosition[oldPage]; const newPos: number = pagesPosition[newPage]; setSlideDirection( @@ -72,7 +79,7 @@ const NavBar = (): JSX.Element => { ); }; - const changePage = function (mouseEvent: any, newPage: Page): void { + const changePage = (event: React.SyntheticEvent, newPage: Page): void => { if (newPage !== 'none') { const slot = garage.getSlot(); handleSlideDirection(page, newPage); @@ -95,102 +102,64 @@ const NavBar = (): JSX.Element => { setOpen(closeAll); }, [page, setOpen]); - const slot = garage.getSlot(); - return ( - - { - setOpen({ ...closeAll, profile: !open.profile }); - }} - icon={ - slot?.hashId ? ( - - ) : ( - <> - ) - } - /> - - + } - iconPosition='start' + icon={} /> - - } - iconPosition='start' + icon={} /> - } - iconPosition='start' + icon={} /> - } - iconPosition='start' + icon={} + disabled={!garage.getSlot()?.getRobot()?.activeOrderId} /> - } - iconPosition='start' - /> - - { - setOpen((open) => { - return { ...open, more: !open.more }; - }); - }} - icon={ - - - - } - iconPosition='start' + icon={} /> - + ); }; From 6729babdb5d42428bdec82e03b2f478e4c4218da Mon Sep 17 00:00:00 2001 From: sahil-tgs Date: Tue, 16 Jul 2024 00:07:33 +0530 Subject: [PATCH 02/13] TopNavBar Added --- frontend/src/basic/Main.tsx | 97 ++++---- frontend/src/basic/TopNavBar/TopNavBar.tsx | 250 +++++++++++++++++++++ frontend/src/basic/TopNavBar/index.tsx | 3 + frontend/src/basic/index.ts | 1 + 4 files changed, 297 insertions(+), 54 deletions(-) create mode 100644 frontend/src/basic/TopNavBar/TopNavBar.tsx create mode 100644 frontend/src/basic/TopNavBar/index.tsx diff --git a/frontend/src/basic/Main.tsx b/frontend/src/basic/Main.tsx index b87d564a9..c48cf9eb3 100644 --- a/frontend/src/basic/Main.tsx +++ b/frontend/src/basic/Main.tsx @@ -1,41 +1,44 @@ import React, { useContext } from 'react'; import { MemoryRouter, BrowserRouter, Routes, Route } from 'react-router-dom'; -import { Box, Slide, Typography, styled } from '@mui/material'; -import { type UseAppStoreType, AppContext, closeAll } from '../contexts/AppContext'; - -import { RobotPage, MakerPage, BookPage, OrderPage, SettingsPage, NavBar, MainDialogs } from './'; -import RobotAvatar from '../components/RobotAvatar'; +import { Box, Slide, styled } from '@mui/material'; +import { type UseAppStoreType, AppContext } from '../contexts/AppContext'; +import { + TopNavBar, + NavBar, + RobotPage, + MakerPage, + BookPage, + OrderPage, + SettingsPage, + MainDialogs, +} from './'; import Notifications from '../components/Notifications'; - import { useTranslation } from 'react-i18next'; import { GarageContext, type UseGarageStoreType } from '../contexts/GarageContext'; const Router = window.NativeRobosats === undefined ? BrowserRouter : MemoryRouter; -const TestnetTypography = styled(Typography)({ - height: 0, -}); - -interface MainBoxProps { - navbarHeight: number; -} - -const MainBox = styled(Box)((props) => ({ - position: 'absolute', - top: '50%', - left: '50%', - transform: `translate(-50%, -50%) translate(0, -${props.navbarHeight / 2}em)`, +const MainContent = styled(Box)(({ theme }) => ({ + marginTop: '100px', + marginBottom: '80px', + padding: theme.spacing(2), + overflowY: 'auto', + overflowX: 'hidden', + height: 'calc(100vh - 180px)', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', })); const Main: React.FC = () => { const { t } = useTranslation(); - const { settings, page, slideDirection, setOpen, windowSize, navbarHeight } = + const { settings, page, slideDirection, setOpen, windowSize } = useContext(AppContext); const { garage } = useContext(GarageContext); return ( - + { @@ -44,36 +47,25 @@ const Main: React.FC = () => { rewards={garage.getSlot()?.getRobot()?.earnedRewards} windowWidth={windowSize?.width} /> - {settings.network === 'testnet' ? ( - - {t('Using Testnet Bitcoin')} - - ) : ( - <> - )} - - + - {['/robot/:token?', '/', ''].map((path, index) => { - return ( - -
- -
- - } - key={index} - /> - ); - })} - + {['/robot/:token?', '/', ''].map((path, index) => ( + +
+ +
+ + } + key={index} + /> + ))} { } /> - { } /> - { } /> - { } />
-
+
diff --git a/frontend/src/basic/TopNavBar/TopNavBar.tsx b/frontend/src/basic/TopNavBar/TopNavBar.tsx new file mode 100644 index 000000000..abe0fdd42 --- /dev/null +++ b/frontend/src/basic/TopNavBar/TopNavBar.tsx @@ -0,0 +1,250 @@ +import React, { useContext, useState } from 'react'; +import { + AppBar, + Toolbar, + Typography, + IconButton, + Box, + useMediaQuery, + styled, + useTheme, + Drawer, + List, + ListItem, + ListItemText, + Divider, +} from '@mui/material'; +import MenuIcon from '@mui/icons-material/Menu'; +import CloseIcon from '@mui/icons-material/Close'; +import AccountCircleIcon from '@mui/icons-material/AccountCircle'; +import { AppContext, type UseAppStoreType, closeAll } from '../../contexts/AppContext'; +import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; +import RobotAvatar from '../../components/RobotAvatar'; +import { RoboSatsTextIcon } from '../../components/Icons'; +import { useTranslation } from 'react-i18next'; + +const NAVBAR_HEIGHT = '64px'; + +const StyledAppBar = styled(AppBar)(({ theme, isMobile, drawerOpen }) => ({ + height: NAVBAR_HEIGHT, + display: 'flex', + justifyContent: 'center', + boxShadow: isMobile ? 'none' : '8px 8px 0px 0px rgba(0,0,0,1)', + backgroundColor: theme.palette.background.paper, + borderBottom: isMobile ? `2px solid ${theme.palette.mode === 'dark' ? '#fff' : '#000'}` : '', + border: !isMobile ? `2px solid ${theme.palette.mode === 'dark' ? '#fff' : '#000'}` : '', + borderRadius: isMobile ? '0' : '1vw', + padding: isMobile ? '0' : '1vh', + top: isMobile ? 0 : theme.spacing(2), + left: '50%', + transform: 'translateX(-50%)', + width: isMobile ? '100%' : 'calc(100% - 64px)', + position: 'fixed', + zIndex: 1100, + ...(drawerOpen && + isMobile && { + background: 'none', + backgroundColor: theme.palette.background.paper, + }), +})); + +const StyledToolbar = styled(Toolbar)(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + width: '100%', + padding: '0 5vw', + minHeight: NAVBAR_HEIGHT, +})); + +const CenterBox = styled(Box)({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + gap: '2vw', + flexGrow: 1, +}); + +const CenterButton = styled(Typography)(({ theme }) => ({ + cursor: 'pointer', + color: theme.palette.mode === 'dark' ? '#fff' : '#000', + '&:hover': { + textDecoration: 'underline', + }, +})); + +const MobileToolbarContent = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', + width: '100%', + marginLeft: theme.spacing(2), +})); + +const TopNavBar = (): JSX.Element => { + const theme = useTheme(); + const { t } = useTranslation(); + const { setOpen, open } = useContext(AppContext); + const { garage } = useContext(GarageContext); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + + const [drawerOpen, setDrawerOpen] = useState(false); + + const slot = garage.getSlot(); + + const navItems = [ + { label: 'Robosats Info', key: 'info' }, + { label: 'Learn Robosats', key: 'learn' }, + { label: 'Community', key: 'community' }, + { label: 'Exchange Summary', key: 'exchange' }, + ]; + + const toggleDrawer = (open: boolean) => () => { + setDrawerOpen(open); + }; + + const handleLogoClick = () => { + setOpen({ ...closeAll, client: !open.client }); + }; + + return ( + <> + + + + + + + + + {isMobile ? ( + <> + + {drawerOpen ? ( + + ) : ( + + )} + + + { + setOpen({ ...closeAll, profile: !open.profile }); + }} + style={{ visibility: slot?.hashId ? 'visible' : 'hidden' }} + > + {slot?.hashId ? ( + + ) : ( + + )} + + + + + + + + + {navItems.map((item) => ( + setOpen({ ...closeAll, [item.key]: !open[item.key] })} + sx={{ + borderBottom: `1px solid ${theme.palette.divider}`, + padding: theme.spacing(2), + }} + > + + + ))} + + + + ) : ( + <> + + + {navItems.map((item) => ( + setOpen({ ...closeAll, [item.key]: !open[item.key] })} + > + {t(item.label)} + + ))} + + { + setOpen({ ...closeAll, profile: !open.profile }); + }} + style={{ visibility: slot?.hashId ? 'visible' : 'hidden' }} + > + {slot?.hashId ? ( + + ) : ( + + )} + + + )} + + + + ); +}; + +export default TopNavBar; diff --git a/frontend/src/basic/TopNavBar/index.tsx b/frontend/src/basic/TopNavBar/index.tsx new file mode 100644 index 000000000..6de1d86b1 --- /dev/null +++ b/frontend/src/basic/TopNavBar/index.tsx @@ -0,0 +1,3 @@ +import TopNavBar from './TopNavBar'; + +export default TopNavBar; diff --git a/frontend/src/basic/index.ts b/frontend/src/basic/index.ts index b2a19d360..680dc23ec 100644 --- a/frontend/src/basic/index.ts +++ b/frontend/src/basic/index.ts @@ -5,3 +5,4 @@ export { default as NavBar } from './NavBar'; export { default as OrderPage } from './OrderPage'; export { default as RobotPage } from './RobotPage'; export { default as SettingsPage } from './SettingsPage'; +export { default as TopNavBar } from './TopNavBar'; From 6a9e6263ad65e253a290d605cdf6dd372ce16594 Mon Sep 17 00:00:00 2001 From: sahil-tgs Date: Tue, 16 Jul 2024 00:10:51 +0530 Subject: [PATCH 03/13] Welcome Update --- frontend/src/basic/RobotPage/Welcome.tsx | 224 ++++++++++++++--------- frontend/src/basic/RobotPage/index.tsx | 105 ++++++----- 2 files changed, 199 insertions(+), 130 deletions(-) diff --git a/frontend/src/basic/RobotPage/Welcome.tsx b/frontend/src/basic/RobotPage/Welcome.tsx index c0b97d83b..948664bf1 100644 --- a/frontend/src/basic/RobotPage/Welcome.tsx +++ b/frontend/src/basic/RobotPage/Welcome.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { Box, Button, Grid, Typography, useTheme } from '@mui/material'; +import { Box, Button, Typography, styled, useTheme } from '@mui/material'; +import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; import { RoboSatsTextIcon } from '../../components/Icons'; -import { FastForward, RocketLaunch, Key } from '@mui/icons-material'; import { genBase62Token } from '../../utils'; interface WelcomeProps { @@ -11,113 +11,167 @@ interface WelcomeProps { width: number; } -const Welcome = ({ setView, width, getGenerateRobot }: WelcomeProps): JSX.Element => { +const BUTTON_COLORS = { + primary: '#2196f3', + secondary: '#9c27b0', + text: '#ffffff', +}; + +const Welcome = ({ setView, getGenerateRobot, width }: WelcomeProps): JSX.Element => { const { t } = useTranslation(); const theme = useTheme(); + const COLORS = { + background: theme.palette.background.paper, + textPrimary: theme.palette.text.primary, + shadow: theme.palette.mode === 'dark' ? '#ffffff' : '#000000', + }; + + const StyledButton = styled(Button)(({ theme }) => ({ + justifyContent: 'space-between', + textAlign: 'left', + padding: theme.spacing(2), + height: '100%', + borderRadius: 0, + border: `2px solid ${theme.palette.mode === 'dark' ? '#ffffff' : '#000000'}`, + boxShadow: `8px 8px 0px 0px ${COLORS.shadow}`, + '&:not(:last-child)': { + borderBottom: 'none', + }, + '&:hover': { + boxShadow: `12px 12px 0px 0px ${COLORS.shadow}`, + }, + })); + return ( - - + - + + + + A Simple and Private ⚡ Lightning P2P Exchange + + + + } + onClick={() => { + setView('onboarding'); }} - /> - - {t('A Simple and Private LN P2P Exchange')} - - - - - + Create a new robot and + learn to use RoboSats. + + Start + + + + } + onClick={() => { + setView('recovery'); + }} > - - - - {t('Create a new robot and learn to use RoboSats')} - - - - - - - - - {t('Recover an existing robot using your token')} - - - - - - - - - - - - + + ▶ Fast Generate Robot + + + + ); }; diff --git a/frontend/src/basic/RobotPage/index.tsx b/frontend/src/basic/RobotPage/index.tsx index 0902c2aae..9bd1f64d8 100644 --- a/frontend/src/basic/RobotPage/index.tsx +++ b/frontend/src/basic/RobotPage/index.tsx @@ -85,11 +85,10 @@ const RobotPage = (): JSX.Element => { if (settings.useProxy && !(window.NativeRobosats === undefined) && !(torStatus === 'ON')) { return ( - @@ -129,54 +128,70 @@ const RobotPage = (): JSX.Element => { - + ); } else { return ( - - {view === 'welcome' ? ( - - ) : null} + + {view === 'welcome' ? ( + + ) : null} - {view === 'onboarding' ? ( - - ) : null} + {view === 'onboarding' ? ( + + ) : null} - {view === 'profile' ? ( - - ) : null} + {view === 'profile' ? ( + + ) : null} - {view === 'recovery' ? ( - - ) : null} - + {view === 'recovery' ? ( + + ) : null} + + ); } }; From d61d4f4e111ce8e350ce1ee6fe40c54172a627a2 Mon Sep 17 00:00:00 2001 From: sahil-tgs Date: Tue, 16 Jul 2024 00:12:28 +0530 Subject: [PATCH 04/13] Onboarding Update --- frontend/src/basic/RobotPage/Onboarding.tsx | 478 ++++++++++++-------- 1 file changed, 278 insertions(+), 200 deletions(-) diff --git a/frontend/src/basic/RobotPage/Onboarding.tsx b/frontend/src/basic/RobotPage/Onboarding.tsx index 1d6d2fad3..f0e209dda 100644 --- a/frontend/src/basic/RobotPage/Onboarding.tsx +++ b/frontend/src/basic/RobotPage/Onboarding.tsx @@ -11,28 +11,117 @@ import { LinearProgress, Link, Typography, - Accordion, - AccordionSummary, - AccordionDetails, + Stepper, + Step, + StepLabel, + StepConnector, + styled, + stepConnectorClasses, + Paper, + useTheme, + useMediaQuery, + IconButton, } from '@mui/material'; -import { type Robot } from '../../models'; -import { Casino, Bolt, Check, Storefront, AddBox, School } from '@mui/icons-material'; +import { + Check, + Casino, + SmartToy, + Storefront, + AddBox, + School, + ContentCopy, +} from '@mui/icons-material'; import RobotAvatar from '../../components/RobotAvatar'; import TokenInput from './TokenInput'; import { genBase62Token } from '../../utils'; -import { NewTabIcon } from '../../components/Icons'; import { AppContext, type UseAppStoreType } from '../../contexts/AppContext'; import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; -interface OnboardingProps { - setView: (state: 'welcome' | 'onboarding' | 'recovery' | 'profile') => void; - robot: Robot; - setRobot: (state: Robot) => void; - inputToken: string; - setInputToken: (state: string) => void; - getGenerateRobot: (token: string) => void; - badToken: string; - baseUrl: string; +const StyledPaper = styled(Paper)(({ theme }) => ({ + backgroundColor: theme.palette.background.paper, + boxShadow: '8px 8px 0px 0px rgba(0, 0, 0, 0.2)', + borderRadius: '16px', + border: '2px solid #000', + padding: theme.spacing(2), + color: theme.palette.text.primary, + width: '100%', + maxWidth: '500px', + margin: '0 auto', +})); + +const StyledButton = styled(Button)(({ theme }) => ({ + justifyContent: 'center', + textAlign: 'center', + padding: theme.spacing(1), + borderRadius: '8px', + border: '2px solid #000', + boxShadow: '4px 4px 0px 0px rgba(0, 0, 0, 0.2)', + textTransform: 'none', + fontWeight: 'bold', + width: '100%', + '&:hover': { + boxShadow: '6px 6px 0px 0px rgba(0, 0, 0, 0.3)', + }, +})); + +const StyledStepConnector = styled(StepConnector)(({ theme }) => ({ + [`&.${stepConnectorClasses.alternativeLabel}`]: { + top: 22, + left: 'calc(-50% + 20px)', + right: 'calc(50% + 20px)', + }, + [`&.${stepConnectorClasses.active}`]: { + [`& .${stepConnectorClasses.line}`]: { + borderColor: theme.palette.primary.main, + }, + }, + [`&.${stepConnectorClasses.completed}`]: { + [`& .${stepConnectorClasses.line}`]: { + borderColor: theme.palette.primary.main, + }, + }, + [`& .${stepConnectorClasses.line}`]: { + borderColor: theme.palette.mode === 'dark' ? theme.palette.grey[800] : '#eaeaf0', + borderTopWidth: 3, + borderRadius: 1, + }, +})); + +const StyledStepIconRoot = styled('div')<{ ownerState: { active?: boolean; completed?: boolean } }>( + ({ theme, ownerState }) => ({ + backgroundColor: theme.palette.mode === 'dark' ? theme.palette.grey[700] : '#ccc', + zIndex: 1, + color: '#fff', + width: 44, + height: 44, + display: 'flex', + borderRadius: '50%', + justifyContent: 'center', + alignItems: 'center', + ...(ownerState.active && { + backgroundColor: theme.palette.primary.main, + boxShadow: '0 4px 10px 0 rgba(0,0,0,.25)', + }), + ...(ownerState.completed && { + backgroundColor: theme.palette.primary.main, + }), + }), +); + +function StyledStepIcon(props: StepIconProps) { + const { active, completed, className } = props; + + const icons: { [index: string]: React.ReactElement } = { + 1: , + 2: , + 3: , + }; + + return ( + + {icons[String(props.icon)]} + + ); } const Onboarding = ({ @@ -44,6 +133,8 @@ const Onboarding = ({ }: OnboardingProps): JSX.Element => { const { t } = useTranslation(); const navigate = useNavigate(); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); const { setPage } = useContext(AppContext); const { garage } = useContext(GarageContext); @@ -63,43 +154,77 @@ const Onboarding = ({ const slot = garage.getSlot(); + const steps = [ + t('1. Generate a token'), + t('2. Meet your robot identity'), + t('3. Browse or create an order'), + ]; + return ( - - - - - {t('1. Generate a token')} - - - - - - - {t( - 'This temporary key gives you access to a unique and private robot identity for your trade.', - )} - - + + } + sx={{ width: '100%', mb: 3 }} + > + {steps.map((label) => ( + + {isMobile ? null : label} + + ))} + + + + {step === '1' && ( + <> + + {t('1. Generate a token')} + + + {t( + 'This temporary key gives you access to a unique and private robot identity for your trade.', + )} + {!generatedToken ? ( - - - + + + {t('Generate Token')} + + ) : ( - - - - - - {`${t('Store it somewhere safe!')} `} + + + + + + {t('Store it somewhere safe!')} + + {t( - `This token is the one and only key to your robot and trade. You will need it later to recover your order or check its status.`, + 'This token is the one and only key to your robot and trade. You will need it later to recover your order or check its status.', )} - - - + + + + + null} + sx={{ flexGrow: 1 }} /> - - - - {t('You can also add your own random characters into the token or')} - - - - - - - + + - - + + )} - - - + + )} - - - - {t('2. Meet your robot identity')} - - - - - - - {slot?.hashId ? ( - t('This is your trading avatar') - ) : ( - <> - {t('Building your robot!')} - - - )} - - - - + {step === '2' && ( + <> + + {t('2. Meet your robot identity')} + + + {slot?.hashId ? t('This is your trading avatar') : t('Building your robot!')} + + {!slot?.hashId && } + - - - {slot?.nickname ? ( - - {t('Hi! My name is')} - -
- - {slot?.nickname} - -
+
+ {slot?.nickname && ( + <> + + {t('Hi! My name is')} - - ) : null} - - - - - - - - - - - - - {t('3. Browse or create an order')} - - - - - - - {t( - 'RoboSats is a peer-to-peer marketplace. You can browse the public offers or create a new one.', - )} - - + + + + {slot.nickname} + + + + + )} + + setStep('3')} + variant='contained' + size='large' + startIcon={} + disabled={!slot?.hashId} + fullWidth={false} + > + {t('Continue')} + + + + )} - - - - + - - - - - {`${t('If you need help on your RoboSats journey join our public support')} `} - - {t('Telegram group')} - - {`, ${t('or visit the robot school for documentation.')} `} - - - - - - - - - - - + + + )} + ); }; From 2f3a353a7e9d9edb3d1d080d11ea89c49c901e7b Mon Sep 17 00:00:00 2001 From: sahil-tgs Date: Wed, 17 Jul 2024 00:00:36 +0530 Subject: [PATCH 05/13] Refactor: Move inline styles to styled components --- frontend/src/basic/Main.tsx | 27 ++- frontend/src/basic/NavBar/NavBar.tsx | 100 ++++---- frontend/src/basic/RobotPage/Onboarding.tsx | 243 +++++++++++--------- frontend/src/basic/RobotPage/Welcome.tsx | 215 +++++++++-------- frontend/src/basic/RobotPage/index.tsx | 94 ++++---- frontend/src/basic/TopNavBar/TopNavBar.tsx | 236 ++++++++++--------- 6 files changed, 487 insertions(+), 428 deletions(-) diff --git a/frontend/src/basic/Main.tsx b/frontend/src/basic/Main.tsx index c48cf9eb3..223b324ff 100644 --- a/frontend/src/basic/Main.tsx +++ b/frontend/src/basic/Main.tsx @@ -18,18 +18,6 @@ import { GarageContext, type UseGarageStoreType } from '../contexts/GarageContex const Router = window.NativeRobosats === undefined ? BrowserRouter : MemoryRouter; -const MainContent = styled(Box)(({ theme }) => ({ - marginTop: '100px', - marginBottom: '80px', - padding: theme.spacing(2), - overflowY: 'auto', - overflowX: 'hidden', - height: 'calc(100vh - 180px)', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', -})); - const Main: React.FC = () => { const { t } = useTranslation(); const { settings, page, slideDirection, setOpen, windowSize } = @@ -130,4 +118,19 @@ const Main: React.FC = () => { ); }; +// Styled components +const MainContent = styled(Box)(({ theme }) => ({ + marginTop: '100px', + marginBottom: '80px', + padding: theme.spacing(2), + overflowY: 'auto', + overflowX: 'hidden', + height: 'calc(100vh - 180px)', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', +})); + export default Main; + + diff --git a/frontend/src/basic/NavBar/NavBar.tsx b/frontend/src/basic/NavBar/NavBar.tsx index c153bf313..1bd7027f7 100644 --- a/frontend/src/basic/NavBar/NavBar.tsx +++ b/frontend/src/basic/NavBar/NavBar.tsx @@ -5,9 +5,9 @@ import { BottomNavigation, BottomNavigationAction, Paper, - styled, useTheme, useMediaQuery, + styled, } from '@mui/material'; import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; import StorefrontOutlinedIcon from '@mui/icons-material/StorefrontOutlined'; @@ -20,24 +20,6 @@ import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageCon import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext'; import { type Page, isPage } from '.'; -const StyledBottomNavigation = styled(BottomNavigation)(({ theme }) => ({ - width: '100%', - maxWidth: '100%', - padding: theme.spacing(0.5, 0), - borderRadius: 'inherit', -})); - -const StyledBottomNavigationAction = styled(BottomNavigationAction)(({ theme }) => ({ - minWidth: 'auto', - padding: theme.spacing(0.5, 0), - '& .MuiBottomNavigationAction-label': { - fontSize: '0.75rem', - }, - '& .MuiSvgIcon-root': { - fontSize: '1.5rem', - }, -})); - const NavBar = (): JSX.Element => { const { t } = useTranslation(); const theme = useTheme(); @@ -103,35 +85,7 @@ const NavBar = (): JSX.Element => { }, [page, setOpen]); return ( - + { icon={} /> - + ); }; -export default NavBar; +// Styled components +const StyledPaper = styled(Paper)<{ $isMobile: boolean }>(({ theme, $isMobile }) => ({ + position: 'fixed', + bottom: 0, + left: 0, + right: 0, + zIndex: 1000, + ...($isMobile + ? { + boxShadow: 'none', + backgroundColor: theme.palette.background.paper, + borderTop: `2px solid black`, + borderRadius: '0', + } + : { + bottom: theme.spacing(2), + left: '50%', + transform: 'translateX(-50%)', + width: '95%', + maxWidth: 600, + borderRadius: '8px', + boxShadow: '8px 8px 0px 0px rgba(0,0,0,1)', + border: `2px solid black`, + backgroundColor: theme.palette.background.paper, + overflow: 'hidden', + }), +})); + +const StyledBottomNavigation = styled(BottomNavigation)(({ theme }) => ({ + width: '100%', + maxWidth: '100%', + padding: theme.spacing(0.5, 0), + borderRadius: 'inherit', +})); + +const StyledBottomNavigationAction = styled(BottomNavigationAction)(({ theme }) => ({ + minWidth: 'auto', + padding: theme.spacing(0.5, 0), + '& .MuiBottomNavigationAction-label': { + fontSize: '0.75rem', + }, + '& .MuiSvgIcon-root': { + fontSize: '1.5rem', + }, +})); + +export default NavBar; \ No newline at end of file diff --git a/frontend/src/basic/RobotPage/Onboarding.tsx b/frontend/src/basic/RobotPage/Onboarding.tsx index f0e209dda..e6c565c8e 100644 --- a/frontend/src/basic/RobotPage/Onboarding.tsx +++ b/frontend/src/basic/RobotPage/Onboarding.tsx @@ -37,91 +37,12 @@ import { genBase62Token } from '../../utils'; import { AppContext, type UseAppStoreType } from '../../contexts/AppContext'; import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; -const StyledPaper = styled(Paper)(({ theme }) => ({ - backgroundColor: theme.palette.background.paper, - boxShadow: '8px 8px 0px 0px rgba(0, 0, 0, 0.2)', - borderRadius: '16px', - border: '2px solid #000', - padding: theme.spacing(2), - color: theme.palette.text.primary, - width: '100%', - maxWidth: '500px', - margin: '0 auto', -})); - -const StyledButton = styled(Button)(({ theme }) => ({ - justifyContent: 'center', - textAlign: 'center', - padding: theme.spacing(1), - borderRadius: '8px', - border: '2px solid #000', - boxShadow: '4px 4px 0px 0px rgba(0, 0, 0, 0.2)', - textTransform: 'none', - fontWeight: 'bold', - width: '100%', - '&:hover': { - boxShadow: '6px 6px 0px 0px rgba(0, 0, 0, 0.3)', - }, -})); - -const StyledStepConnector = styled(StepConnector)(({ theme }) => ({ - [`&.${stepConnectorClasses.alternativeLabel}`]: { - top: 22, - left: 'calc(-50% + 20px)', - right: 'calc(50% + 20px)', - }, - [`&.${stepConnectorClasses.active}`]: { - [`& .${stepConnectorClasses.line}`]: { - borderColor: theme.palette.primary.main, - }, - }, - [`&.${stepConnectorClasses.completed}`]: { - [`& .${stepConnectorClasses.line}`]: { - borderColor: theme.palette.primary.main, - }, - }, - [`& .${stepConnectorClasses.line}`]: { - borderColor: theme.palette.mode === 'dark' ? theme.palette.grey[800] : '#eaeaf0', - borderTopWidth: 3, - borderRadius: 1, - }, -})); - -const StyledStepIconRoot = styled('div')<{ ownerState: { active?: boolean; completed?: boolean } }>( - ({ theme, ownerState }) => ({ - backgroundColor: theme.palette.mode === 'dark' ? theme.palette.grey[700] : '#ccc', - zIndex: 1, - color: '#fff', - width: 44, - height: 44, - display: 'flex', - borderRadius: '50%', - justifyContent: 'center', - alignItems: 'center', - ...(ownerState.active && { - backgroundColor: theme.palette.primary.main, - boxShadow: '0 4px 10px 0 rgba(0,0,0,.25)', - }), - ...(ownerState.completed && { - backgroundColor: theme.palette.primary.main, - }), - }), -); - -function StyledStepIcon(props: StepIconProps) { - const { active, completed, className } = props; - - const icons: { [index: string]: React.ReactElement } = { - 1: , - 2: , - 3: , - }; - - return ( - - {icons[String(props.icon)]} - - ); +interface OnboardingProps { + setView: (view: string) => void; + inputToken: string; + setInputToken: (token: string) => void; + badToken: boolean; + getGenerateRobot: (token: string) => void; } const Onboarding = ({ @@ -161,17 +82,7 @@ const Onboarding = ({ ]; return ( - + {!slot?.hashId && } - + + + {slot?.nickname && ( <> @@ -293,11 +199,11 @@ const Onboarding = ({ {t('Hi! My name is')} - + {slot.nickname} - + )} @@ -315,7 +221,6 @@ const Onboarding = ({ )} - {step === '3' && ( <> @@ -382,8 +287,122 @@ const Onboarding = ({ )} - + ); }; +// Styled components +const StyledPaper = styled(Paper)(({ theme }) => ({ + backgroundColor: theme.palette.background.paper, + boxShadow: '8px 8px 0px 0px rgba(0, 0, 0, 0.2)', + borderRadius: '16px', + border: '2px solid #000', + padding: theme.spacing(2), + color: theme.palette.text.primary, + width: '100%', + maxWidth: '500px', + margin: '0 auto', +})); + +const StyledButton = styled(Button)(({ theme }) => ({ + justifyContent: 'center', + textAlign: 'center', + padding: theme.spacing(1), + borderRadius: '8px', + border: '2px solid #000', + boxShadow: '4px 4px 0px 0px rgba(0, 0, 0, 0.2)', + textTransform: 'none', + fontWeight: 'bold', + width: '100%', + '&:hover': { + boxShadow: '6px 6px 0px 0px rgba(0, 0, 0, 0.3)', + }, +})); + +const OnboardingContainer = styled(Box)(({ theme }) => ({ + marginTop: theme.spacing(3), + marginBottom: theme.spacing(3), + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(2), + width: '100%', +})); + +const RobotAvatarContainer = styled(Box)(({ theme }) => ({ + width: '150px', + height: '150px', + border: '2px solid #555', + borderRadius: '50%', + boxShadow: '0 4px 10px rgba(0,0,0,0.3)', +})); + +const NicknameContainer = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', +})); + +const StyledStepConnector = styled(StepConnector)(({ theme }) => ({ + [`&.${stepConnectorClasses.alternativeLabel}`]: { + top: 22, + left: 'calc(-50% + 20px)', + right: 'calc(50% + 20px)', + }, + [`&.${stepConnectorClasses.active}`]: { + [`& .${stepConnectorClasses.line}`]: { + borderColor: theme.palette.primary.main, + }, + }, + [`&.${stepConnectorClasses.completed}`]: { + [`& .${stepConnectorClasses.line}`]: { + borderColor: theme.palette.primary.main, + }, + }, + [`& .${stepConnectorClasses.line}`]: { + borderColor: theme.palette.mode === 'dark' ? theme.palette.grey[800] : '#eaeaf0', + borderTopWidth: 3, + borderRadius: 1, + }, +})); + +const StyledStepIconRoot = styled('div')<{ ownerState: { active?: boolean; completed?: boolean } }>( + ({ theme, ownerState }) => ({ + backgroundColor: theme.palette.mode === 'dark' ? theme.palette.grey[700] : '#ccc', + zIndex: 1, + color: '#fff', + width: 44, + height: 44, + display: 'flex', + borderRadius: '50%', + justifyContent: 'center', + alignItems: 'center', + ...(ownerState.active && { + backgroundColor: theme.palette.primary.main, + boxShadow: '0 4px 10px 0 rgba(0,0,0,.25)', + }), + ...(ownerState.completed && { + backgroundColor: theme.palette.primary.main, + }), + }), +); + +function StyledStepIcon(props: StepIconProps) { + const { active, completed, className } = props; + + const icons: { [index: string]: React.ReactElement } = { + 1: , + 2: , + 3: , + }; + + return ( + + {icons[String(props.icon)]} + + ); +} + export default Onboarding; + diff --git a/frontend/src/basic/RobotPage/Welcome.tsx b/frontend/src/basic/RobotPage/Welcome.tsx index 948664bf1..541598e36 100644 --- a/frontend/src/basic/RobotPage/Welcome.tsx +++ b/frontend/src/basic/RobotPage/Welcome.tsx @@ -27,140 +27,67 @@ const Welcome = ({ setView, getGenerateRobot, width }: WelcomeProps): JSX.Elemen shadow: theme.palette.mode === 'dark' ? '#ffffff' : '#000000', }; - const StyledButton = styled(Button)(({ theme }) => ({ - justifyContent: 'space-between', - textAlign: 'left', - padding: theme.spacing(2), - height: '100%', - borderRadius: 0, - border: `2px solid ${theme.palette.mode === 'dark' ? '#ffffff' : '#000000'}`, - boxShadow: `8px 8px 0px 0px ${COLORS.shadow}`, - '&:not(:last-child)': { - borderBottom: 'none', - }, - '&:hover': { - boxShadow: `12px 12px 0px 0px ${COLORS.shadow}`, - }, - })); - return ( - - + + - - - + + + A Simple and Private ⚡ Lightning P2P Exchange - - + + } - onClick={() => { - setView('onboarding'); - }} + onClick={() => setView('onboarding')} > - + Create a new robot and learn to use RoboSats. Start - + } - onClick={() => { - setView('recovery'); - }} + onClick={() => setView('recovery')} > - + Recover an existing Robot using your token. Recover - + { setView('profile'); getGenerateRobot(genBase62Token(36)); @@ -170,9 +97,93 @@ const Welcome = ({ setView, getGenerateRobot, width }: WelcomeProps): JSX.Elemen ▶ Fast Generate Robot - - + + ); }; -export default Welcome; +// Styled components +const WelcomeContainer = styled(Box)(({ theme }) => ({ + width: '100%', + maxWidth: 800, + margin: '0 auto', + display: 'flex', + flexDirection: 'column', + marginBottom: theme.spacing(8), + [theme.breakpoints.up('md')]: { + flexDirection: 'row', + }, +})); + +const LogoSection = styled(Box)<{ colors: typeof COLORS }>(({ theme, colors }) => ({ + flexGrow: 1, + flexBasis: 0, + backgroundColor: colors.background, + border: `2px solid ${colors.textPrimary}`, + borderRight: 'none', + borderRadius: '8px 8px 0 0', + boxShadow: `8px 8px 0px 0px ${colors.shadow}`, + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + padding: theme.spacing(4), + [theme.breakpoints.up('md')]: { + borderRadius: '8px 0 0 8px', + }, +})); + +const LogoBox = styled(Box)({ + width: '80%', + maxWidth: '400px', + height: 'auto', +}); + +const StyledRoboSatsTextIcon = styled(RoboSatsTextIcon)({ + fill: 'url(#linearColors)', + width: '100%', + height: 'auto', +}); + +const ButtonsSection = styled(Box)(({ theme }) => ({ + flexGrow: 1, + flexBasis: 0, + display: 'flex', + flexDirection: 'column', + backgroundColor: 'transparent', +})); + +const StyledButton = styled(Button)<{ + $buttonColor: string; + $textColor: string; + $shadowColor: string; + $borderRadius: { xs: string; md: string }; +}>(({ theme, $buttonColor, $textColor, $shadowColor, $borderRadius }) => ({ + justifyContent: 'space-between', + textAlign: 'left', + padding: theme.spacing(2), + height: '100%', + borderRadius: 0, + border: `2px solid ${theme.palette.mode === 'dark' ? '#ffffff' : '#000000'}`, + boxShadow: `8px 8px 0px 0px ${$shadowColor}`, + '&:not(:last-child)': { + borderBottom: 'none', + }, + '&:hover': { + backgroundColor: $buttonColor, + boxShadow: `12px 12px 0px 0px ${$shadowColor}`, + }, + backgroundColor: $buttonColor, + color: $textColor, + borderRadius: $borderRadius.xs, + [theme.breakpoints.up('md')]: { + borderRadius: $borderRadius.md, + }, +})); + +const ButtonContent = styled(Box)({ + display: 'flex', + flexDirection: 'column', +}); + +export default Welcome; \ No newline at end of file diff --git a/frontend/src/basic/RobotPage/index.tsx b/frontend/src/basic/RobotPage/index.tsx index 9bd1f64d8..5015c849e 100644 --- a/frontend/src/basic/RobotPage/index.tsx +++ b/frontend/src/basic/RobotPage/index.tsx @@ -9,6 +9,7 @@ import { Typography, useTheme, AlertTitle, + styled, } from '@mui/material'; import { useParams } from 'react-router-dom'; @@ -85,12 +86,7 @@ const RobotPage = (): JSX.Element => { if (settings.useProxy && !(window.NativeRobosats === undefined) && !(torStatus === 'ON')) { return ( - + @@ -105,8 +101,8 @@ const RobotPage = (): JSX.Element => { - - + + { left: '0.7em', }} /> - + @@ -128,39 +124,17 @@ const RobotPage = (): JSX.Element => { - + ); } else { return ( - - - {view === 'welcome' ? ( + + + {view === 'welcome' && ( - ) : null} + )} - {view === 'onboarding' ? ( + {view === 'onboarding' && ( { setInputToken={setInputToken} getGenerateRobot={getGenerateRobot} /> - ) : null} + )} - {view === 'profile' ? ( + {view === 'profile' && ( { setInputToken={setInputToken} getGenerateRobot={getGenerateRobot} /> - ) : null} + )} - {view === 'recovery' ? ( + {view === 'recovery' && ( { setInputToken={setInputToken} getRecoverRobot={getGenerateRobot} /> - ) : null} - - + )} + + ); } }; +// Styled components +const StyledConnectingBox = styled(Box)({ + width: '100vw', + height: 'auto', +}); + +const StyledTorIconBox = styled(Box)({ + position: 'fixed', + top: '4.6em', +}); + +const StyledMainBox = styled(Box)({ + width: '100vw', + height: 'auto', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + padding: '2em', +}); + +const StyledPaper = styled(Paper)(({ theme }) => ({ + width: '80vw', + maxWidth: '1200px', + maxHeight: '85vh', + overflow: 'auto', + overflowX: 'clip', + backgroundColor: 'transparent', + border: 'none', + boxShadow: 'none', + padding: '1em', +})); + export default RobotPage; diff --git a/frontend/src/basic/TopNavBar/TopNavBar.tsx b/frontend/src/basic/TopNavBar/TopNavBar.tsx index abe0fdd42..de41ac63c 100644 --- a/frontend/src/basic/TopNavBar/TopNavBar.tsx +++ b/frontend/src/basic/TopNavBar/TopNavBar.tsx @@ -23,63 +23,6 @@ import RobotAvatar from '../../components/RobotAvatar'; import { RoboSatsTextIcon } from '../../components/Icons'; import { useTranslation } from 'react-i18next'; -const NAVBAR_HEIGHT = '64px'; - -const StyledAppBar = styled(AppBar)(({ theme, isMobile, drawerOpen }) => ({ - height: NAVBAR_HEIGHT, - display: 'flex', - justifyContent: 'center', - boxShadow: isMobile ? 'none' : '8px 8px 0px 0px rgba(0,0,0,1)', - backgroundColor: theme.palette.background.paper, - borderBottom: isMobile ? `2px solid ${theme.palette.mode === 'dark' ? '#fff' : '#000'}` : '', - border: !isMobile ? `2px solid ${theme.palette.mode === 'dark' ? '#fff' : '#000'}` : '', - borderRadius: isMobile ? '0' : '1vw', - padding: isMobile ? '0' : '1vh', - top: isMobile ? 0 : theme.spacing(2), - left: '50%', - transform: 'translateX(-50%)', - width: isMobile ? '100%' : 'calc(100% - 64px)', - position: 'fixed', - zIndex: 1100, - ...(drawerOpen && - isMobile && { - background: 'none', - backgroundColor: theme.palette.background.paper, - }), -})); - -const StyledToolbar = styled(Toolbar)(({ theme }) => ({ - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - width: '100%', - padding: '0 5vw', - minHeight: NAVBAR_HEIGHT, -})); - -const CenterBox = styled(Box)({ - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - gap: '2vw', - flexGrow: 1, -}); - -const CenterButton = styled(Typography)(({ theme }) => ({ - cursor: 'pointer', - color: theme.palette.mode === 'dark' ? '#fff' : '#000', - '&:hover': { - textDecoration: 'underline', - }, -})); - -const MobileToolbarContent = styled(Box)(({ theme }) => ({ - display: 'flex', - justifyContent: 'flex-end', - alignItems: 'center', - width: '100%', - marginLeft: theme.spacing(2), -})); const TopNavBar = (): JSX.Element => { const theme = useTheme(); @@ -112,8 +55,8 @@ const TopNavBar = (): JSX.Element => { @@ -126,9 +69,9 @@ const TopNavBar = (): JSX.Element => { <> {drawerOpen ? ( - + ) : ( - + )} @@ -140,76 +83,43 @@ const TopNavBar = (): JSX.Element => { style={{ visibility: slot?.hashId ? 'visible' : 'hidden' }} > {slot?.hashId ? ( - ) : ( - + )} - - - - + + + {navItems.map((item) => ( - setOpen({ ...closeAll, [item.key]: !open[item.key] })} - sx={{ - borderBottom: `1px solid ${theme.palette.divider}`, - padding: theme.spacing(2), - }} > - + ))} - + ) : ( <> - + {navItems.map((item) => ( { style={{ visibility: slot?.hashId ? 'visible' : 'hidden' }} > {slot?.hashId ? ( - ) : ( - + )} @@ -247,4 +156,115 @@ const TopNavBar = (): JSX.Element => { ); }; -export default TopNavBar; +// Styled components +const NAVBAR_HEIGHT = '64px'; + +const StyledAppBar = styled(AppBar)<{ $isMobile: boolean; $drawerOpen: boolean }>( + ({ theme, $isMobile, $drawerOpen }) => ({ + height: NAVBAR_HEIGHT, + display: 'flex', + justifyContent: 'center', + boxShadow: $isMobile ? 'none' : '8px 8px 0px 0px rgba(0,0,0,1)', + backgroundColor: theme.palette.background.paper, + borderBottom: $isMobile ? `2px solid ${theme.palette.mode === 'dark' ? '#fff' : '#000'}` : '', + border: !$isMobile ? `2px solid ${theme.palette.mode === 'dark' ? '#fff' : '#000'}` : '', + borderRadius: $isMobile ? '0' : '1vw', + padding: $isMobile ? '0' : '1vh', + top: $isMobile ? 0 : theme.spacing(2), + left: '50%', + transform: 'translateX(-50%)', + width: $isMobile ? '100%' : 'calc(100% - 64px)', + position: 'fixed', + zIndex: 1100, + ...($drawerOpen && + $isMobile && { + background: 'none', + backgroundColor: theme.palette.background.paper, + }), + }) +); + +const StyledToolbar = styled(Toolbar)(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + width: '100%', + padding: '0 5vw', + minHeight: NAVBAR_HEIGHT, +})); + +const CenterBox = styled(Box)({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + gap: '2vw', + flexGrow: 1, +}); + +const CenterButton = styled(Typography)(({ theme }) => ({ + cursor: 'pointer', + color: theme.palette.mode === 'dark' ? '#fff' : '#000', + '&:hover': { + textDecoration: 'underline', + }, +})); + +const MobileToolbarContent = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', + width: '100%', + marginLeft: theme.spacing(2), +})); + +const StyledIcon = styled('svg')<{ $isDark: boolean }>(({ $isDark }) => ({ + color: $isDark ? '#fff' : '#000', +})); + +const StyledRobotAvatar = styled(RobotAvatar)({ + width: '2.5em', + height: '2.5em', +}); + +const StyledAccountIcon = styled(AccountCircleIcon)({ + fontSize: '1.5em', +}); + +const StyledDrawer = styled(Drawer)(({ theme }) => ({ + '& .MuiDrawer-paper': { + marginTop: NAVBAR_HEIGHT, + borderLeft: '2px solid black', + borderRight: '2px solid black', + borderBottom: '2px solid black', + width: '100%', + maxWidth: '300px', + }, +})); + +const StyledListItem = styled(ListItem)(({ theme }) => ({ + borderBottom: `1px solid ${theme.palette.divider}`, + padding: theme.spacing(2), +})); + +const StyledLogoListItem = styled(ListItem)(({ theme }) => ({ + borderBottom: `1px solid ${theme.palette.divider}`, + padding: theme.spacing(2), + justifyContent: 'center', +})); + +const StyledDesktopRoboSatsTextIcon = styled(RoboSatsTextIcon)(({ theme }) => ({ + height: '1.5em', + width: 'auto', + cursor: 'pointer', + marginLeft: theme.spacing(-1), + fill: 'url(#linearColors)', +})); + +const StyledDrawerRoboSatsTextIcon = styled(RoboSatsTextIcon)(({ theme }) => ({ + height: '2em', + width: 'auto', + cursor: 'pointer', + fill: 'url(#linearColors)', +})); + +export default TopNavBar; \ No newline at end of file From 49458a5442e9e84fa7ed472b2e817909e107bddb Mon Sep 17 00:00:00 2001 From: sahil-tgs Date: Wed, 17 Jul 2024 02:24:34 +0530 Subject: [PATCH 06/13] fix: Adjust component heights to prevent nav bar overlap --- frontend/src/basic/BookPage/index.tsx | 19 +++++++++---------- frontend/src/basic/SettingsPage/index.tsx | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/frontend/src/basic/BookPage/index.tsx b/frontend/src/basic/BookPage/index.tsx index ed74cc240..be33604a8 100644 --- a/frontend/src/basic/BookPage/index.tsx +++ b/frontend/src/basic/BookPage/index.tsx @@ -3,17 +3,16 @@ import { useTranslation } from 'react-i18next'; import { Button, Grid, ButtonGroup, Dialog, Box } from '@mui/material'; import { useNavigate } from 'react-router-dom'; import DepthChart from '../../components/Charts/DepthChart'; - import { NoRobotDialog } from '../../components/Dialogs'; import MakerForm from '../../components/MakerForm'; import BookTable from '../../components/BookTable'; // Icons import { BarChart, FormatListBulleted, Map } from '@mui/icons-material'; -import { AppContext, type UseAppStoreType } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; import MapChart from '../../components/Charts/MapChart'; -import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext'; -import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; +import { FederationContext, UseFederationStoreType } from '../../contexts/FederationContext'; +import { GarageContext, UseGarageStoreType } from '../../contexts/GarageContext'; const BookPage = (): JSX.Element => { const { windowSize } = useContext(AppContext); @@ -126,7 +125,7 @@ const BookPage = (): JSX.Element => { { @@ -153,19 +152,19 @@ const BookPage = (): JSX.Element => { ) : view === 'depth' ? ( ) : view === 'map' ? ( ) : ( { const { windowSize, navbarHeight } = useContext(AppContext); const { federation, addNewCoordinator } = useContext(FederationContext); - const maxHeight = (windowSize.height - navbarHeight) * 0.85 - 3; + const maxHeight = (windowSize.height * 0.65) const [newAlias, setNewAlias] = useState(''); const [newUrl, setNewUrl] = useState(''); const [error, setError] = useState(); From bbe0ffa68293d9e6d56fbf55e6b01c6c8d6f7aaf Mon Sep 17 00:00:00 2001 From: sahil-tgs Date: Tue, 16 Jul 2024 00:03:28 +0530 Subject: [PATCH 07/13] NavBar update --- frontend/src/basic/NavBar/NavBar.tsx | 195 +++++++++++---------------- 1 file changed, 82 insertions(+), 113 deletions(-) diff --git a/frontend/src/basic/NavBar/NavBar.tsx b/frontend/src/basic/NavBar/NavBar.tsx index 9cabcfd03..c153bf313 100644 --- a/frontend/src/basic/NavBar/NavBar.tsx +++ b/frontend/src/basic/NavBar/NavBar.tsx @@ -1,44 +1,55 @@ import React, { useContext, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useLocation, useNavigate } from 'react-router-dom'; -import { Tabs, Tab, Paper, useTheme } from '@mui/material'; -import MoreTooltip from './MoreTooltip'; - -import { type Page, isPage } from '.'; - import { - SettingsApplications, - SmartToy, - Storefront, - AddBox, - Assignment, - MoreHoriz, -} from '@mui/icons-material'; -import RobotAvatar from '../../components/RobotAvatar'; + BottomNavigation, + BottomNavigationAction, + Paper, + styled, + useTheme, + useMediaQuery, +} from '@mui/material'; +import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; +import StorefrontOutlinedIcon from '@mui/icons-material/StorefrontOutlined'; +import AddBoxOutlinedIcon from '@mui/icons-material/AddBoxOutlined'; +import AssignmentOutlinedIcon from '@mui/icons-material/AssignmentOutlined'; +import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined'; + import { AppContext, type UseAppStoreType, closeAll } from '../../contexts/AppContext'; import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext'; +import { type Page, isPage } from '.'; + +const StyledBottomNavigation = styled(BottomNavigation)(({ theme }) => ({ + width: '100%', + maxWidth: '100%', + padding: theme.spacing(0.5, 0), + borderRadius: 'inherit', +})); + +const StyledBottomNavigationAction = styled(BottomNavigationAction)(({ theme }) => ({ + minWidth: 'auto', + padding: theme.spacing(0.5, 0), + '& .MuiBottomNavigationAction-label': { + fontSize: '0.75rem', + }, + '& .MuiSvgIcon-root': { + fontSize: '1.5rem', + }, +})); const NavBar = (): JSX.Element => { - const theme = useTheme(); const { t } = useTranslation(); - const { page, setPage, settings, setSlideDirection, open, setOpen, windowSize, navbarHeight } = + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + const { page, setPage, setSlideDirection, open, setOpen } = useContext(AppContext); - const { garage, robotUpdatedAt } = useContext(GarageContext); + const { garage } = useContext(GarageContext); const { setCurrentOrderId } = useContext(FederationContext); const navigate = useNavigate(); const location = useLocation(); - const smallBar = windowSize?.width < 50; - const color = settings.network === 'mainnet' ? 'primary' : 'secondary'; - - const tabSx = smallBar - ? { - position: 'relative', - bottom: garage.getSlot()?.hashId ? '0.9em' : '0.13em', - minWidth: '1em', - } - : { position: 'relative', bottom: '1em', minWidth: '2em' }; + const [value, setValue] = React.useState(page); const pagesPosition = { robot: 1, @@ -49,11 +60,6 @@ const NavBar = (): JSX.Element => { }; useEffect(() => { - // re-render on orde rand robot updated at for latest orderId in tab - }, [robotUpdatedAt]); - - useEffect(() => { - // change tab (page) into the current route const pathPage: Page | string = location.pathname.split('/')[1]; if (pathPage === 'index.html') { navigate('/robot'); @@ -62,9 +68,10 @@ const NavBar = (): JSX.Element => { if (isPage(pathPage)) { setPage(pathPage); } - }, [location]); + setValue(pathPage as Page); + }, [location, navigate, setPage]); - const handleSlideDirection = function (oldPage: Page, newPage: Page): void { + const handleSlideDirection = (oldPage: Page, newPage: Page): void => { const oldPos: number = pagesPosition[oldPage]; const newPos: number = pagesPosition[newPage]; setSlideDirection( @@ -72,7 +79,7 @@ const NavBar = (): JSX.Element => { ); }; - const changePage = function (mouseEvent: any, newPage: Page): void { + const changePage = (event: React.SyntheticEvent, newPage: Page): void => { if (newPage !== 'none') { const slot = garage.getSlot(); handleSlideDirection(page, newPage); @@ -95,102 +102,64 @@ const NavBar = (): JSX.Element => { setOpen(closeAll); }, [page, setOpen]); - const slot = garage.getSlot(); - return ( - - { - setOpen({ ...closeAll, profile: !open.profile }); - }} - icon={ - slot?.hashId ? ( - - ) : ( - <> - ) - } - /> - - + } - iconPosition='start' + icon={} /> - - } - iconPosition='start' + icon={} /> - } - iconPosition='start' + icon={} /> - } - iconPosition='start' + icon={} + disabled={!garage.getSlot()?.getRobot()?.activeOrderId} /> - } - iconPosition='start' - /> - - { - setOpen((open) => { - return { ...open, more: !open.more }; - }); - }} - icon={ - - - - } - iconPosition='start' + icon={} /> - + ); }; From e08bfa99d2a568ff8562962ad6ab76f1c00ccf01 Mon Sep 17 00:00:00 2001 From: sahil-tgs Date: Tue, 16 Jul 2024 00:07:33 +0530 Subject: [PATCH 08/13] TopNavBar Added --- frontend/src/basic/Main.tsx | 97 ++++---- frontend/src/basic/TopNavBar/TopNavBar.tsx | 250 +++++++++++++++++++++ frontend/src/basic/TopNavBar/index.tsx | 3 + frontend/src/basic/index.ts | 1 + 4 files changed, 297 insertions(+), 54 deletions(-) create mode 100644 frontend/src/basic/TopNavBar/TopNavBar.tsx create mode 100644 frontend/src/basic/TopNavBar/index.tsx diff --git a/frontend/src/basic/Main.tsx b/frontend/src/basic/Main.tsx index b87d564a9..c48cf9eb3 100644 --- a/frontend/src/basic/Main.tsx +++ b/frontend/src/basic/Main.tsx @@ -1,41 +1,44 @@ import React, { useContext } from 'react'; import { MemoryRouter, BrowserRouter, Routes, Route } from 'react-router-dom'; -import { Box, Slide, Typography, styled } from '@mui/material'; -import { type UseAppStoreType, AppContext, closeAll } from '../contexts/AppContext'; - -import { RobotPage, MakerPage, BookPage, OrderPage, SettingsPage, NavBar, MainDialogs } from './'; -import RobotAvatar from '../components/RobotAvatar'; +import { Box, Slide, styled } from '@mui/material'; +import { type UseAppStoreType, AppContext } from '../contexts/AppContext'; +import { + TopNavBar, + NavBar, + RobotPage, + MakerPage, + BookPage, + OrderPage, + SettingsPage, + MainDialogs, +} from './'; import Notifications from '../components/Notifications'; - import { useTranslation } from 'react-i18next'; import { GarageContext, type UseGarageStoreType } from '../contexts/GarageContext'; const Router = window.NativeRobosats === undefined ? BrowserRouter : MemoryRouter; -const TestnetTypography = styled(Typography)({ - height: 0, -}); - -interface MainBoxProps { - navbarHeight: number; -} - -const MainBox = styled(Box)((props) => ({ - position: 'absolute', - top: '50%', - left: '50%', - transform: `translate(-50%, -50%) translate(0, -${props.navbarHeight / 2}em)`, +const MainContent = styled(Box)(({ theme }) => ({ + marginTop: '100px', + marginBottom: '80px', + padding: theme.spacing(2), + overflowY: 'auto', + overflowX: 'hidden', + height: 'calc(100vh - 180px)', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', })); const Main: React.FC = () => { const { t } = useTranslation(); - const { settings, page, slideDirection, setOpen, windowSize, navbarHeight } = + const { settings, page, slideDirection, setOpen, windowSize } = useContext(AppContext); const { garage } = useContext(GarageContext); return ( - + { @@ -44,36 +47,25 @@ const Main: React.FC = () => { rewards={garage.getSlot()?.getRobot()?.earnedRewards} windowWidth={windowSize?.width} /> - {settings.network === 'testnet' ? ( - - {t('Using Testnet Bitcoin')} - - ) : ( - <> - )} - - + - {['/robot/:token?', '/', ''].map((path, index) => { - return ( - -
- -
- - } - key={index} - /> - ); - })} - + {['/robot/:token?', '/', ''].map((path, index) => ( + +
+ +
+ + } + key={index} + /> + ))} { } /> - { } /> - { } /> - { } />
-
+ diff --git a/frontend/src/basic/TopNavBar/TopNavBar.tsx b/frontend/src/basic/TopNavBar/TopNavBar.tsx new file mode 100644 index 000000000..abe0fdd42 --- /dev/null +++ b/frontend/src/basic/TopNavBar/TopNavBar.tsx @@ -0,0 +1,250 @@ +import React, { useContext, useState } from 'react'; +import { + AppBar, + Toolbar, + Typography, + IconButton, + Box, + useMediaQuery, + styled, + useTheme, + Drawer, + List, + ListItem, + ListItemText, + Divider, +} from '@mui/material'; +import MenuIcon from '@mui/icons-material/Menu'; +import CloseIcon from '@mui/icons-material/Close'; +import AccountCircleIcon from '@mui/icons-material/AccountCircle'; +import { AppContext, type UseAppStoreType, closeAll } from '../../contexts/AppContext'; +import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; +import RobotAvatar from '../../components/RobotAvatar'; +import { RoboSatsTextIcon } from '../../components/Icons'; +import { useTranslation } from 'react-i18next'; + +const NAVBAR_HEIGHT = '64px'; + +const StyledAppBar = styled(AppBar)(({ theme, isMobile, drawerOpen }) => ({ + height: NAVBAR_HEIGHT, + display: 'flex', + justifyContent: 'center', + boxShadow: isMobile ? 'none' : '8px 8px 0px 0px rgba(0,0,0,1)', + backgroundColor: theme.palette.background.paper, + borderBottom: isMobile ? `2px solid ${theme.palette.mode === 'dark' ? '#fff' : '#000'}` : '', + border: !isMobile ? `2px solid ${theme.palette.mode === 'dark' ? '#fff' : '#000'}` : '', + borderRadius: isMobile ? '0' : '1vw', + padding: isMobile ? '0' : '1vh', + top: isMobile ? 0 : theme.spacing(2), + left: '50%', + transform: 'translateX(-50%)', + width: isMobile ? '100%' : 'calc(100% - 64px)', + position: 'fixed', + zIndex: 1100, + ...(drawerOpen && + isMobile && { + background: 'none', + backgroundColor: theme.palette.background.paper, + }), +})); + +const StyledToolbar = styled(Toolbar)(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + width: '100%', + padding: '0 5vw', + minHeight: NAVBAR_HEIGHT, +})); + +const CenterBox = styled(Box)({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + gap: '2vw', + flexGrow: 1, +}); + +const CenterButton = styled(Typography)(({ theme }) => ({ + cursor: 'pointer', + color: theme.palette.mode === 'dark' ? '#fff' : '#000', + '&:hover': { + textDecoration: 'underline', + }, +})); + +const MobileToolbarContent = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', + width: '100%', + marginLeft: theme.spacing(2), +})); + +const TopNavBar = (): JSX.Element => { + const theme = useTheme(); + const { t } = useTranslation(); + const { setOpen, open } = useContext(AppContext); + const { garage } = useContext(GarageContext); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + + const [drawerOpen, setDrawerOpen] = useState(false); + + const slot = garage.getSlot(); + + const navItems = [ + { label: 'Robosats Info', key: 'info' }, + { label: 'Learn Robosats', key: 'learn' }, + { label: 'Community', key: 'community' }, + { label: 'Exchange Summary', key: 'exchange' }, + ]; + + const toggleDrawer = (open: boolean) => () => { + setDrawerOpen(open); + }; + + const handleLogoClick = () => { + setOpen({ ...closeAll, client: !open.client }); + }; + + return ( + <> + + + + + + + + + {isMobile ? ( + <> + + {drawerOpen ? ( + + ) : ( + + )} + + + { + setOpen({ ...closeAll, profile: !open.profile }); + }} + style={{ visibility: slot?.hashId ? 'visible' : 'hidden' }} + > + {slot?.hashId ? ( + + ) : ( + + )} + + + + + + + + + {navItems.map((item) => ( + setOpen({ ...closeAll, [item.key]: !open[item.key] })} + sx={{ + borderBottom: `1px solid ${theme.palette.divider}`, + padding: theme.spacing(2), + }} + > + + + ))} + + + + ) : ( + <> + + + {navItems.map((item) => ( + setOpen({ ...closeAll, [item.key]: !open[item.key] })} + > + {t(item.label)} + + ))} + + { + setOpen({ ...closeAll, profile: !open.profile }); + }} + style={{ visibility: slot?.hashId ? 'visible' : 'hidden' }} + > + {slot?.hashId ? ( + + ) : ( + + )} + + + )} + + + + ); +}; + +export default TopNavBar; diff --git a/frontend/src/basic/TopNavBar/index.tsx b/frontend/src/basic/TopNavBar/index.tsx new file mode 100644 index 000000000..6de1d86b1 --- /dev/null +++ b/frontend/src/basic/TopNavBar/index.tsx @@ -0,0 +1,3 @@ +import TopNavBar from './TopNavBar'; + +export default TopNavBar; diff --git a/frontend/src/basic/index.ts b/frontend/src/basic/index.ts index b2a19d360..680dc23ec 100644 --- a/frontend/src/basic/index.ts +++ b/frontend/src/basic/index.ts @@ -5,3 +5,4 @@ export { default as NavBar } from './NavBar'; export { default as OrderPage } from './OrderPage'; export { default as RobotPage } from './RobotPage'; export { default as SettingsPage } from './SettingsPage'; +export { default as TopNavBar } from './TopNavBar'; From 9e2b4f11d2cce1fd06340caedcf70453576227c5 Mon Sep 17 00:00:00 2001 From: sahil-tgs Date: Tue, 16 Jul 2024 00:10:51 +0530 Subject: [PATCH 09/13] Welcome Update --- frontend/src/basic/RobotPage/Welcome.tsx | 224 ++++++++++++++--------- frontend/src/basic/RobotPage/index.tsx | 105 ++++++----- 2 files changed, 199 insertions(+), 130 deletions(-) diff --git a/frontend/src/basic/RobotPage/Welcome.tsx b/frontend/src/basic/RobotPage/Welcome.tsx index c0b97d83b..948664bf1 100644 --- a/frontend/src/basic/RobotPage/Welcome.tsx +++ b/frontend/src/basic/RobotPage/Welcome.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { Box, Button, Grid, Typography, useTheme } from '@mui/material'; +import { Box, Button, Typography, styled, useTheme } from '@mui/material'; +import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; import { RoboSatsTextIcon } from '../../components/Icons'; -import { FastForward, RocketLaunch, Key } from '@mui/icons-material'; import { genBase62Token } from '../../utils'; interface WelcomeProps { @@ -11,113 +11,167 @@ interface WelcomeProps { width: number; } -const Welcome = ({ setView, width, getGenerateRobot }: WelcomeProps): JSX.Element => { +const BUTTON_COLORS = { + primary: '#2196f3', + secondary: '#9c27b0', + text: '#ffffff', +}; + +const Welcome = ({ setView, getGenerateRobot, width }: WelcomeProps): JSX.Element => { const { t } = useTranslation(); const theme = useTheme(); + const COLORS = { + background: theme.palette.background.paper, + textPrimary: theme.palette.text.primary, + shadow: theme.palette.mode === 'dark' ? '#ffffff' : '#000000', + }; + + const StyledButton = styled(Button)(({ theme }) => ({ + justifyContent: 'space-between', + textAlign: 'left', + padding: theme.spacing(2), + height: '100%', + borderRadius: 0, + border: `2px solid ${theme.palette.mode === 'dark' ? '#ffffff' : '#000000'}`, + boxShadow: `8px 8px 0px 0px ${COLORS.shadow}`, + '&:not(:last-child)': { + borderBottom: 'none', + }, + '&:hover': { + boxShadow: `12px 12px 0px 0px ${COLORS.shadow}`, + }, + })); + return ( - - + - + + + + A Simple and Private ⚡ Lightning P2P Exchange + + + + } + onClick={() => { + setView('onboarding'); }} - /> - - {t('A Simple and Private LN P2P Exchange')} - - - - - + Create a new robot and + learn to use RoboSats. + + Start + + + + } + onClick={() => { + setView('recovery'); + }} > - - - - {t('Create a new robot and learn to use RoboSats')} - - - - - - - - - {t('Recover an existing robot using your token')} - - - - - - - - - - - - + + ▶ Fast Generate Robot + + + + ); }; diff --git a/frontend/src/basic/RobotPage/index.tsx b/frontend/src/basic/RobotPage/index.tsx index 0902c2aae..9bd1f64d8 100644 --- a/frontend/src/basic/RobotPage/index.tsx +++ b/frontend/src/basic/RobotPage/index.tsx @@ -85,11 +85,10 @@ const RobotPage = (): JSX.Element => { if (settings.useProxy && !(window.NativeRobosats === undefined) && !(torStatus === 'ON')) { return ( - @@ -129,54 +128,70 @@ const RobotPage = (): JSX.Element => { - + ); } else { return ( - - {view === 'welcome' ? ( - - ) : null} + + {view === 'welcome' ? ( + + ) : null} - {view === 'onboarding' ? ( - - ) : null} + {view === 'onboarding' ? ( + + ) : null} - {view === 'profile' ? ( - - ) : null} + {view === 'profile' ? ( + + ) : null} - {view === 'recovery' ? ( - - ) : null} - + {view === 'recovery' ? ( + + ) : null} + + ); } }; From e8d53c9bf97c2af5d09243ff791db70c45ec259f Mon Sep 17 00:00:00 2001 From: sahil-tgs Date: Tue, 16 Jul 2024 00:12:28 +0530 Subject: [PATCH 10/13] Onboarding Update --- frontend/src/basic/RobotPage/Onboarding.tsx | 478 ++++++++++++-------- 1 file changed, 278 insertions(+), 200 deletions(-) diff --git a/frontend/src/basic/RobotPage/Onboarding.tsx b/frontend/src/basic/RobotPage/Onboarding.tsx index 1d6d2fad3..f0e209dda 100644 --- a/frontend/src/basic/RobotPage/Onboarding.tsx +++ b/frontend/src/basic/RobotPage/Onboarding.tsx @@ -11,28 +11,117 @@ import { LinearProgress, Link, Typography, - Accordion, - AccordionSummary, - AccordionDetails, + Stepper, + Step, + StepLabel, + StepConnector, + styled, + stepConnectorClasses, + Paper, + useTheme, + useMediaQuery, + IconButton, } from '@mui/material'; -import { type Robot } from '../../models'; -import { Casino, Bolt, Check, Storefront, AddBox, School } from '@mui/icons-material'; +import { + Check, + Casino, + SmartToy, + Storefront, + AddBox, + School, + ContentCopy, +} from '@mui/icons-material'; import RobotAvatar from '../../components/RobotAvatar'; import TokenInput from './TokenInput'; import { genBase62Token } from '../../utils'; -import { NewTabIcon } from '../../components/Icons'; import { AppContext, type UseAppStoreType } from '../../contexts/AppContext'; import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; -interface OnboardingProps { - setView: (state: 'welcome' | 'onboarding' | 'recovery' | 'profile') => void; - robot: Robot; - setRobot: (state: Robot) => void; - inputToken: string; - setInputToken: (state: string) => void; - getGenerateRobot: (token: string) => void; - badToken: string; - baseUrl: string; +const StyledPaper = styled(Paper)(({ theme }) => ({ + backgroundColor: theme.palette.background.paper, + boxShadow: '8px 8px 0px 0px rgba(0, 0, 0, 0.2)', + borderRadius: '16px', + border: '2px solid #000', + padding: theme.spacing(2), + color: theme.palette.text.primary, + width: '100%', + maxWidth: '500px', + margin: '0 auto', +})); + +const StyledButton = styled(Button)(({ theme }) => ({ + justifyContent: 'center', + textAlign: 'center', + padding: theme.spacing(1), + borderRadius: '8px', + border: '2px solid #000', + boxShadow: '4px 4px 0px 0px rgba(0, 0, 0, 0.2)', + textTransform: 'none', + fontWeight: 'bold', + width: '100%', + '&:hover': { + boxShadow: '6px 6px 0px 0px rgba(0, 0, 0, 0.3)', + }, +})); + +const StyledStepConnector = styled(StepConnector)(({ theme }) => ({ + [`&.${stepConnectorClasses.alternativeLabel}`]: { + top: 22, + left: 'calc(-50% + 20px)', + right: 'calc(50% + 20px)', + }, + [`&.${stepConnectorClasses.active}`]: { + [`& .${stepConnectorClasses.line}`]: { + borderColor: theme.palette.primary.main, + }, + }, + [`&.${stepConnectorClasses.completed}`]: { + [`& .${stepConnectorClasses.line}`]: { + borderColor: theme.palette.primary.main, + }, + }, + [`& .${stepConnectorClasses.line}`]: { + borderColor: theme.palette.mode === 'dark' ? theme.palette.grey[800] : '#eaeaf0', + borderTopWidth: 3, + borderRadius: 1, + }, +})); + +const StyledStepIconRoot = styled('div')<{ ownerState: { active?: boolean; completed?: boolean } }>( + ({ theme, ownerState }) => ({ + backgroundColor: theme.palette.mode === 'dark' ? theme.palette.grey[700] : '#ccc', + zIndex: 1, + color: '#fff', + width: 44, + height: 44, + display: 'flex', + borderRadius: '50%', + justifyContent: 'center', + alignItems: 'center', + ...(ownerState.active && { + backgroundColor: theme.palette.primary.main, + boxShadow: '0 4px 10px 0 rgba(0,0,0,.25)', + }), + ...(ownerState.completed && { + backgroundColor: theme.palette.primary.main, + }), + }), +); + +function StyledStepIcon(props: StepIconProps) { + const { active, completed, className } = props; + + const icons: { [index: string]: React.ReactElement } = { + 1: , + 2: , + 3: , + }; + + return ( + + {icons[String(props.icon)]} + + ); } const Onboarding = ({ @@ -44,6 +133,8 @@ const Onboarding = ({ }: OnboardingProps): JSX.Element => { const { t } = useTranslation(); const navigate = useNavigate(); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); const { setPage } = useContext(AppContext); const { garage } = useContext(GarageContext); @@ -63,43 +154,77 @@ const Onboarding = ({ const slot = garage.getSlot(); + const steps = [ + t('1. Generate a token'), + t('2. Meet your robot identity'), + t('3. Browse or create an order'), + ]; + return ( - - - - - {t('1. Generate a token')} - - - - - - - {t( - 'This temporary key gives you access to a unique and private robot identity for your trade.', - )} - - + + } + sx={{ width: '100%', mb: 3 }} + > + {steps.map((label) => ( + + {isMobile ? null : label} + + ))} + + + + {step === '1' && ( + <> + + {t('1. Generate a token')} + + + {t( + 'This temporary key gives you access to a unique and private robot identity for your trade.', + )} + {!generatedToken ? ( - - - + + + {t('Generate Token')} + + ) : ( - - - - - - {`${t('Store it somewhere safe!')} `} + + + + + + {t('Store it somewhere safe!')} + + {t( - `This token is the one and only key to your robot and trade. You will need it later to recover your order or check its status.`, + 'This token is the one and only key to your robot and trade. You will need it later to recover your order or check its status.', )} - - - + + + + + null} + sx={{ flexGrow: 1 }} /> - - - - {t('You can also add your own random characters into the token or')} - - - - - - - + + - - + + )} - - - + + )} - - - - {t('2. Meet your robot identity')} - - - - - - - {slot?.hashId ? ( - t('This is your trading avatar') - ) : ( - <> - {t('Building your robot!')} - - - )} - - - - + {step === '2' && ( + <> + + {t('2. Meet your robot identity')} + + + {slot?.hashId ? t('This is your trading avatar') : t('Building your robot!')} + + {!slot?.hashId && } + - - - {slot?.nickname ? ( - - {t('Hi! My name is')} - -
- - {slot?.nickname} - -
+
+ {slot?.nickname && ( + <> + + {t('Hi! My name is')} - - ) : null} - - - - - - - - - - - - - {t('3. Browse or create an order')} - - - - - - - {t( - 'RoboSats is a peer-to-peer marketplace. You can browse the public offers or create a new one.', - )} - - + + + + {slot.nickname} + + + + + )} + + setStep('3')} + variant='contained' + size='large' + startIcon={} + disabled={!slot?.hashId} + fullWidth={false} + > + {t('Continue')} + + + + )} - - - - + - - - - - {`${t('If you need help on your RoboSats journey join our public support')} `} - - {t('Telegram group')} - - {`, ${t('or visit the robot school for documentation.')} `} - - - - - - - - - - - + + + )} + ); }; From 6fc5f906dc4bc9f6c0fcfea04622ee94be78fe9c Mon Sep 17 00:00:00 2001 From: sahil-tgs Date: Wed, 17 Jul 2024 00:00:36 +0530 Subject: [PATCH 11/13] Refactor: Move inline styles to styled components --- frontend/src/basic/Main.tsx | 27 ++- frontend/src/basic/NavBar/NavBar.tsx | 100 ++++---- frontend/src/basic/RobotPage/Onboarding.tsx | 243 +++++++++++--------- frontend/src/basic/RobotPage/Welcome.tsx | 215 +++++++++-------- frontend/src/basic/RobotPage/index.tsx | 94 ++++---- frontend/src/basic/TopNavBar/TopNavBar.tsx | 236 ++++++++++--------- 6 files changed, 487 insertions(+), 428 deletions(-) diff --git a/frontend/src/basic/Main.tsx b/frontend/src/basic/Main.tsx index c48cf9eb3..223b324ff 100644 --- a/frontend/src/basic/Main.tsx +++ b/frontend/src/basic/Main.tsx @@ -18,18 +18,6 @@ import { GarageContext, type UseGarageStoreType } from '../contexts/GarageContex const Router = window.NativeRobosats === undefined ? BrowserRouter : MemoryRouter; -const MainContent = styled(Box)(({ theme }) => ({ - marginTop: '100px', - marginBottom: '80px', - padding: theme.spacing(2), - overflowY: 'auto', - overflowX: 'hidden', - height: 'calc(100vh - 180px)', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', -})); - const Main: React.FC = () => { const { t } = useTranslation(); const { settings, page, slideDirection, setOpen, windowSize } = @@ -130,4 +118,19 @@ const Main: React.FC = () => { ); }; +// Styled components +const MainContent = styled(Box)(({ theme }) => ({ + marginTop: '100px', + marginBottom: '80px', + padding: theme.spacing(2), + overflowY: 'auto', + overflowX: 'hidden', + height: 'calc(100vh - 180px)', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', +})); + export default Main; + + diff --git a/frontend/src/basic/NavBar/NavBar.tsx b/frontend/src/basic/NavBar/NavBar.tsx index c153bf313..1bd7027f7 100644 --- a/frontend/src/basic/NavBar/NavBar.tsx +++ b/frontend/src/basic/NavBar/NavBar.tsx @@ -5,9 +5,9 @@ import { BottomNavigation, BottomNavigationAction, Paper, - styled, useTheme, useMediaQuery, + styled, } from '@mui/material'; import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; import StorefrontOutlinedIcon from '@mui/icons-material/StorefrontOutlined'; @@ -20,24 +20,6 @@ import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageCon import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext'; import { type Page, isPage } from '.'; -const StyledBottomNavigation = styled(BottomNavigation)(({ theme }) => ({ - width: '100%', - maxWidth: '100%', - padding: theme.spacing(0.5, 0), - borderRadius: 'inherit', -})); - -const StyledBottomNavigationAction = styled(BottomNavigationAction)(({ theme }) => ({ - minWidth: 'auto', - padding: theme.spacing(0.5, 0), - '& .MuiBottomNavigationAction-label': { - fontSize: '0.75rem', - }, - '& .MuiSvgIcon-root': { - fontSize: '1.5rem', - }, -})); - const NavBar = (): JSX.Element => { const { t } = useTranslation(); const theme = useTheme(); @@ -103,35 +85,7 @@ const NavBar = (): JSX.Element => { }, [page, setOpen]); return ( - + { icon={} /> - + ); }; -export default NavBar; +// Styled components +const StyledPaper = styled(Paper)<{ $isMobile: boolean }>(({ theme, $isMobile }) => ({ + position: 'fixed', + bottom: 0, + left: 0, + right: 0, + zIndex: 1000, + ...($isMobile + ? { + boxShadow: 'none', + backgroundColor: theme.palette.background.paper, + borderTop: `2px solid black`, + borderRadius: '0', + } + : { + bottom: theme.spacing(2), + left: '50%', + transform: 'translateX(-50%)', + width: '95%', + maxWidth: 600, + borderRadius: '8px', + boxShadow: '8px 8px 0px 0px rgba(0,0,0,1)', + border: `2px solid black`, + backgroundColor: theme.palette.background.paper, + overflow: 'hidden', + }), +})); + +const StyledBottomNavigation = styled(BottomNavigation)(({ theme }) => ({ + width: '100%', + maxWidth: '100%', + padding: theme.spacing(0.5, 0), + borderRadius: 'inherit', +})); + +const StyledBottomNavigationAction = styled(BottomNavigationAction)(({ theme }) => ({ + minWidth: 'auto', + padding: theme.spacing(0.5, 0), + '& .MuiBottomNavigationAction-label': { + fontSize: '0.75rem', + }, + '& .MuiSvgIcon-root': { + fontSize: '1.5rem', + }, +})); + +export default NavBar; \ No newline at end of file diff --git a/frontend/src/basic/RobotPage/Onboarding.tsx b/frontend/src/basic/RobotPage/Onboarding.tsx index f0e209dda..e6c565c8e 100644 --- a/frontend/src/basic/RobotPage/Onboarding.tsx +++ b/frontend/src/basic/RobotPage/Onboarding.tsx @@ -37,91 +37,12 @@ import { genBase62Token } from '../../utils'; import { AppContext, type UseAppStoreType } from '../../contexts/AppContext'; import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; -const StyledPaper = styled(Paper)(({ theme }) => ({ - backgroundColor: theme.palette.background.paper, - boxShadow: '8px 8px 0px 0px rgba(0, 0, 0, 0.2)', - borderRadius: '16px', - border: '2px solid #000', - padding: theme.spacing(2), - color: theme.palette.text.primary, - width: '100%', - maxWidth: '500px', - margin: '0 auto', -})); - -const StyledButton = styled(Button)(({ theme }) => ({ - justifyContent: 'center', - textAlign: 'center', - padding: theme.spacing(1), - borderRadius: '8px', - border: '2px solid #000', - boxShadow: '4px 4px 0px 0px rgba(0, 0, 0, 0.2)', - textTransform: 'none', - fontWeight: 'bold', - width: '100%', - '&:hover': { - boxShadow: '6px 6px 0px 0px rgba(0, 0, 0, 0.3)', - }, -})); - -const StyledStepConnector = styled(StepConnector)(({ theme }) => ({ - [`&.${stepConnectorClasses.alternativeLabel}`]: { - top: 22, - left: 'calc(-50% + 20px)', - right: 'calc(50% + 20px)', - }, - [`&.${stepConnectorClasses.active}`]: { - [`& .${stepConnectorClasses.line}`]: { - borderColor: theme.palette.primary.main, - }, - }, - [`&.${stepConnectorClasses.completed}`]: { - [`& .${stepConnectorClasses.line}`]: { - borderColor: theme.palette.primary.main, - }, - }, - [`& .${stepConnectorClasses.line}`]: { - borderColor: theme.palette.mode === 'dark' ? theme.palette.grey[800] : '#eaeaf0', - borderTopWidth: 3, - borderRadius: 1, - }, -})); - -const StyledStepIconRoot = styled('div')<{ ownerState: { active?: boolean; completed?: boolean } }>( - ({ theme, ownerState }) => ({ - backgroundColor: theme.palette.mode === 'dark' ? theme.palette.grey[700] : '#ccc', - zIndex: 1, - color: '#fff', - width: 44, - height: 44, - display: 'flex', - borderRadius: '50%', - justifyContent: 'center', - alignItems: 'center', - ...(ownerState.active && { - backgroundColor: theme.palette.primary.main, - boxShadow: '0 4px 10px 0 rgba(0,0,0,.25)', - }), - ...(ownerState.completed && { - backgroundColor: theme.palette.primary.main, - }), - }), -); - -function StyledStepIcon(props: StepIconProps) { - const { active, completed, className } = props; - - const icons: { [index: string]: React.ReactElement } = { - 1: , - 2: , - 3: , - }; - - return ( - - {icons[String(props.icon)]} - - ); +interface OnboardingProps { + setView: (view: string) => void; + inputToken: string; + setInputToken: (token: string) => void; + badToken: boolean; + getGenerateRobot: (token: string) => void; } const Onboarding = ({ @@ -161,17 +82,7 @@ const Onboarding = ({ ]; return ( - + {!slot?.hashId && } - + + + {slot?.nickname && ( <> @@ -293,11 +199,11 @@ const Onboarding = ({ {t('Hi! My name is')} - + {slot.nickname} - + )} @@ -315,7 +221,6 @@ const Onboarding = ({ )} - {step === '3' && ( <> @@ -382,8 +287,122 @@ const Onboarding = ({ )} - + ); }; +// Styled components +const StyledPaper = styled(Paper)(({ theme }) => ({ + backgroundColor: theme.palette.background.paper, + boxShadow: '8px 8px 0px 0px rgba(0, 0, 0, 0.2)', + borderRadius: '16px', + border: '2px solid #000', + padding: theme.spacing(2), + color: theme.palette.text.primary, + width: '100%', + maxWidth: '500px', + margin: '0 auto', +})); + +const StyledButton = styled(Button)(({ theme }) => ({ + justifyContent: 'center', + textAlign: 'center', + padding: theme.spacing(1), + borderRadius: '8px', + border: '2px solid #000', + boxShadow: '4px 4px 0px 0px rgba(0, 0, 0, 0.2)', + textTransform: 'none', + fontWeight: 'bold', + width: '100%', + '&:hover': { + boxShadow: '6px 6px 0px 0px rgba(0, 0, 0, 0.3)', + }, +})); + +const OnboardingContainer = styled(Box)(({ theme }) => ({ + marginTop: theme.spacing(3), + marginBottom: theme.spacing(3), + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(2), + width: '100%', +})); + +const RobotAvatarContainer = styled(Box)(({ theme }) => ({ + width: '150px', + height: '150px', + border: '2px solid #555', + borderRadius: '50%', + boxShadow: '0 4px 10px rgba(0,0,0,0.3)', +})); + +const NicknameContainer = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', +})); + +const StyledStepConnector = styled(StepConnector)(({ theme }) => ({ + [`&.${stepConnectorClasses.alternativeLabel}`]: { + top: 22, + left: 'calc(-50% + 20px)', + right: 'calc(50% + 20px)', + }, + [`&.${stepConnectorClasses.active}`]: { + [`& .${stepConnectorClasses.line}`]: { + borderColor: theme.palette.primary.main, + }, + }, + [`&.${stepConnectorClasses.completed}`]: { + [`& .${stepConnectorClasses.line}`]: { + borderColor: theme.palette.primary.main, + }, + }, + [`& .${stepConnectorClasses.line}`]: { + borderColor: theme.palette.mode === 'dark' ? theme.palette.grey[800] : '#eaeaf0', + borderTopWidth: 3, + borderRadius: 1, + }, +})); + +const StyledStepIconRoot = styled('div')<{ ownerState: { active?: boolean; completed?: boolean } }>( + ({ theme, ownerState }) => ({ + backgroundColor: theme.palette.mode === 'dark' ? theme.palette.grey[700] : '#ccc', + zIndex: 1, + color: '#fff', + width: 44, + height: 44, + display: 'flex', + borderRadius: '50%', + justifyContent: 'center', + alignItems: 'center', + ...(ownerState.active && { + backgroundColor: theme.palette.primary.main, + boxShadow: '0 4px 10px 0 rgba(0,0,0,.25)', + }), + ...(ownerState.completed && { + backgroundColor: theme.palette.primary.main, + }), + }), +); + +function StyledStepIcon(props: StepIconProps) { + const { active, completed, className } = props; + + const icons: { [index: string]: React.ReactElement } = { + 1: , + 2: , + 3: , + }; + + return ( + + {icons[String(props.icon)]} + + ); +} + export default Onboarding; + diff --git a/frontend/src/basic/RobotPage/Welcome.tsx b/frontend/src/basic/RobotPage/Welcome.tsx index 948664bf1..541598e36 100644 --- a/frontend/src/basic/RobotPage/Welcome.tsx +++ b/frontend/src/basic/RobotPage/Welcome.tsx @@ -27,140 +27,67 @@ const Welcome = ({ setView, getGenerateRobot, width }: WelcomeProps): JSX.Elemen shadow: theme.palette.mode === 'dark' ? '#ffffff' : '#000000', }; - const StyledButton = styled(Button)(({ theme }) => ({ - justifyContent: 'space-between', - textAlign: 'left', - padding: theme.spacing(2), - height: '100%', - borderRadius: 0, - border: `2px solid ${theme.palette.mode === 'dark' ? '#ffffff' : '#000000'}`, - boxShadow: `8px 8px 0px 0px ${COLORS.shadow}`, - '&:not(:last-child)': { - borderBottom: 'none', - }, - '&:hover': { - boxShadow: `12px 12px 0px 0px ${COLORS.shadow}`, - }, - })); - return ( - - + + - - - + + + A Simple and Private ⚡ Lightning P2P Exchange - - + + } - onClick={() => { - setView('onboarding'); - }} + onClick={() => setView('onboarding')} > - + Create a new robot and learn to use RoboSats. Start - + } - onClick={() => { - setView('recovery'); - }} + onClick={() => setView('recovery')} > - + Recover an existing Robot using your token. Recover - + { setView('profile'); getGenerateRobot(genBase62Token(36)); @@ -170,9 +97,93 @@ const Welcome = ({ setView, getGenerateRobot, width }: WelcomeProps): JSX.Elemen ▶ Fast Generate Robot - - + + ); }; -export default Welcome; +// Styled components +const WelcomeContainer = styled(Box)(({ theme }) => ({ + width: '100%', + maxWidth: 800, + margin: '0 auto', + display: 'flex', + flexDirection: 'column', + marginBottom: theme.spacing(8), + [theme.breakpoints.up('md')]: { + flexDirection: 'row', + }, +})); + +const LogoSection = styled(Box)<{ colors: typeof COLORS }>(({ theme, colors }) => ({ + flexGrow: 1, + flexBasis: 0, + backgroundColor: colors.background, + border: `2px solid ${colors.textPrimary}`, + borderRight: 'none', + borderRadius: '8px 8px 0 0', + boxShadow: `8px 8px 0px 0px ${colors.shadow}`, + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + padding: theme.spacing(4), + [theme.breakpoints.up('md')]: { + borderRadius: '8px 0 0 8px', + }, +})); + +const LogoBox = styled(Box)({ + width: '80%', + maxWidth: '400px', + height: 'auto', +}); + +const StyledRoboSatsTextIcon = styled(RoboSatsTextIcon)({ + fill: 'url(#linearColors)', + width: '100%', + height: 'auto', +}); + +const ButtonsSection = styled(Box)(({ theme }) => ({ + flexGrow: 1, + flexBasis: 0, + display: 'flex', + flexDirection: 'column', + backgroundColor: 'transparent', +})); + +const StyledButton = styled(Button)<{ + $buttonColor: string; + $textColor: string; + $shadowColor: string; + $borderRadius: { xs: string; md: string }; +}>(({ theme, $buttonColor, $textColor, $shadowColor, $borderRadius }) => ({ + justifyContent: 'space-between', + textAlign: 'left', + padding: theme.spacing(2), + height: '100%', + borderRadius: 0, + border: `2px solid ${theme.palette.mode === 'dark' ? '#ffffff' : '#000000'}`, + boxShadow: `8px 8px 0px 0px ${$shadowColor}`, + '&:not(:last-child)': { + borderBottom: 'none', + }, + '&:hover': { + backgroundColor: $buttonColor, + boxShadow: `12px 12px 0px 0px ${$shadowColor}`, + }, + backgroundColor: $buttonColor, + color: $textColor, + borderRadius: $borderRadius.xs, + [theme.breakpoints.up('md')]: { + borderRadius: $borderRadius.md, + }, +})); + +const ButtonContent = styled(Box)({ + display: 'flex', + flexDirection: 'column', +}); + +export default Welcome; \ No newline at end of file diff --git a/frontend/src/basic/RobotPage/index.tsx b/frontend/src/basic/RobotPage/index.tsx index 9bd1f64d8..5015c849e 100644 --- a/frontend/src/basic/RobotPage/index.tsx +++ b/frontend/src/basic/RobotPage/index.tsx @@ -9,6 +9,7 @@ import { Typography, useTheme, AlertTitle, + styled, } from '@mui/material'; import { useParams } from 'react-router-dom'; @@ -85,12 +86,7 @@ const RobotPage = (): JSX.Element => { if (settings.useProxy && !(window.NativeRobosats === undefined) && !(torStatus === 'ON')) { return ( - + @@ -105,8 +101,8 @@ const RobotPage = (): JSX.Element => { - - + + { left: '0.7em', }} /> - + @@ -128,39 +124,17 @@ const RobotPage = (): JSX.Element => { - + ); } else { return ( - - - {view === 'welcome' ? ( + + + {view === 'welcome' && ( - ) : null} + )} - {view === 'onboarding' ? ( + {view === 'onboarding' && ( { setInputToken={setInputToken} getGenerateRobot={getGenerateRobot} /> - ) : null} + )} - {view === 'profile' ? ( + {view === 'profile' && ( { setInputToken={setInputToken} getGenerateRobot={getGenerateRobot} /> - ) : null} + )} - {view === 'recovery' ? ( + {view === 'recovery' && ( { setInputToken={setInputToken} getRecoverRobot={getGenerateRobot} /> - ) : null} - - + )} + + ); } }; +// Styled components +const StyledConnectingBox = styled(Box)({ + width: '100vw', + height: 'auto', +}); + +const StyledTorIconBox = styled(Box)({ + position: 'fixed', + top: '4.6em', +}); + +const StyledMainBox = styled(Box)({ + width: '100vw', + height: 'auto', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + padding: '2em', +}); + +const StyledPaper = styled(Paper)(({ theme }) => ({ + width: '80vw', + maxWidth: '1200px', + maxHeight: '85vh', + overflow: 'auto', + overflowX: 'clip', + backgroundColor: 'transparent', + border: 'none', + boxShadow: 'none', + padding: '1em', +})); + export default RobotPage; diff --git a/frontend/src/basic/TopNavBar/TopNavBar.tsx b/frontend/src/basic/TopNavBar/TopNavBar.tsx index abe0fdd42..de41ac63c 100644 --- a/frontend/src/basic/TopNavBar/TopNavBar.tsx +++ b/frontend/src/basic/TopNavBar/TopNavBar.tsx @@ -23,63 +23,6 @@ import RobotAvatar from '../../components/RobotAvatar'; import { RoboSatsTextIcon } from '../../components/Icons'; import { useTranslation } from 'react-i18next'; -const NAVBAR_HEIGHT = '64px'; - -const StyledAppBar = styled(AppBar)(({ theme, isMobile, drawerOpen }) => ({ - height: NAVBAR_HEIGHT, - display: 'flex', - justifyContent: 'center', - boxShadow: isMobile ? 'none' : '8px 8px 0px 0px rgba(0,0,0,1)', - backgroundColor: theme.palette.background.paper, - borderBottom: isMobile ? `2px solid ${theme.palette.mode === 'dark' ? '#fff' : '#000'}` : '', - border: !isMobile ? `2px solid ${theme.palette.mode === 'dark' ? '#fff' : '#000'}` : '', - borderRadius: isMobile ? '0' : '1vw', - padding: isMobile ? '0' : '1vh', - top: isMobile ? 0 : theme.spacing(2), - left: '50%', - transform: 'translateX(-50%)', - width: isMobile ? '100%' : 'calc(100% - 64px)', - position: 'fixed', - zIndex: 1100, - ...(drawerOpen && - isMobile && { - background: 'none', - backgroundColor: theme.palette.background.paper, - }), -})); - -const StyledToolbar = styled(Toolbar)(({ theme }) => ({ - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - width: '100%', - padding: '0 5vw', - minHeight: NAVBAR_HEIGHT, -})); - -const CenterBox = styled(Box)({ - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - gap: '2vw', - flexGrow: 1, -}); - -const CenterButton = styled(Typography)(({ theme }) => ({ - cursor: 'pointer', - color: theme.palette.mode === 'dark' ? '#fff' : '#000', - '&:hover': { - textDecoration: 'underline', - }, -})); - -const MobileToolbarContent = styled(Box)(({ theme }) => ({ - display: 'flex', - justifyContent: 'flex-end', - alignItems: 'center', - width: '100%', - marginLeft: theme.spacing(2), -})); const TopNavBar = (): JSX.Element => { const theme = useTheme(); @@ -112,8 +55,8 @@ const TopNavBar = (): JSX.Element => { @@ -126,9 +69,9 @@ const TopNavBar = (): JSX.Element => { <> {drawerOpen ? ( - + ) : ( - + )} @@ -140,76 +83,43 @@ const TopNavBar = (): JSX.Element => { style={{ visibility: slot?.hashId ? 'visible' : 'hidden' }} > {slot?.hashId ? ( - ) : ( - + )} - - - - + + + {navItems.map((item) => ( - setOpen({ ...closeAll, [item.key]: !open[item.key] })} - sx={{ - borderBottom: `1px solid ${theme.palette.divider}`, - padding: theme.spacing(2), - }} > - + ))} - + ) : ( <> - + {navItems.map((item) => ( { style={{ visibility: slot?.hashId ? 'visible' : 'hidden' }} > {slot?.hashId ? ( - ) : ( - + )} @@ -247,4 +156,115 @@ const TopNavBar = (): JSX.Element => { ); }; -export default TopNavBar; +// Styled components +const NAVBAR_HEIGHT = '64px'; + +const StyledAppBar = styled(AppBar)<{ $isMobile: boolean; $drawerOpen: boolean }>( + ({ theme, $isMobile, $drawerOpen }) => ({ + height: NAVBAR_HEIGHT, + display: 'flex', + justifyContent: 'center', + boxShadow: $isMobile ? 'none' : '8px 8px 0px 0px rgba(0,0,0,1)', + backgroundColor: theme.palette.background.paper, + borderBottom: $isMobile ? `2px solid ${theme.palette.mode === 'dark' ? '#fff' : '#000'}` : '', + border: !$isMobile ? `2px solid ${theme.palette.mode === 'dark' ? '#fff' : '#000'}` : '', + borderRadius: $isMobile ? '0' : '1vw', + padding: $isMobile ? '0' : '1vh', + top: $isMobile ? 0 : theme.spacing(2), + left: '50%', + transform: 'translateX(-50%)', + width: $isMobile ? '100%' : 'calc(100% - 64px)', + position: 'fixed', + zIndex: 1100, + ...($drawerOpen && + $isMobile && { + background: 'none', + backgroundColor: theme.palette.background.paper, + }), + }) +); + +const StyledToolbar = styled(Toolbar)(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + width: '100%', + padding: '0 5vw', + minHeight: NAVBAR_HEIGHT, +})); + +const CenterBox = styled(Box)({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + gap: '2vw', + flexGrow: 1, +}); + +const CenterButton = styled(Typography)(({ theme }) => ({ + cursor: 'pointer', + color: theme.palette.mode === 'dark' ? '#fff' : '#000', + '&:hover': { + textDecoration: 'underline', + }, +})); + +const MobileToolbarContent = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', + width: '100%', + marginLeft: theme.spacing(2), +})); + +const StyledIcon = styled('svg')<{ $isDark: boolean }>(({ $isDark }) => ({ + color: $isDark ? '#fff' : '#000', +})); + +const StyledRobotAvatar = styled(RobotAvatar)({ + width: '2.5em', + height: '2.5em', +}); + +const StyledAccountIcon = styled(AccountCircleIcon)({ + fontSize: '1.5em', +}); + +const StyledDrawer = styled(Drawer)(({ theme }) => ({ + '& .MuiDrawer-paper': { + marginTop: NAVBAR_HEIGHT, + borderLeft: '2px solid black', + borderRight: '2px solid black', + borderBottom: '2px solid black', + width: '100%', + maxWidth: '300px', + }, +})); + +const StyledListItem = styled(ListItem)(({ theme }) => ({ + borderBottom: `1px solid ${theme.palette.divider}`, + padding: theme.spacing(2), +})); + +const StyledLogoListItem = styled(ListItem)(({ theme }) => ({ + borderBottom: `1px solid ${theme.palette.divider}`, + padding: theme.spacing(2), + justifyContent: 'center', +})); + +const StyledDesktopRoboSatsTextIcon = styled(RoboSatsTextIcon)(({ theme }) => ({ + height: '1.5em', + width: 'auto', + cursor: 'pointer', + marginLeft: theme.spacing(-1), + fill: 'url(#linearColors)', +})); + +const StyledDrawerRoboSatsTextIcon = styled(RoboSatsTextIcon)(({ theme }) => ({ + height: '2em', + width: 'auto', + cursor: 'pointer', + fill: 'url(#linearColors)', +})); + +export default TopNavBar; \ No newline at end of file From 13a681b9b3bbacfa68dd8af46319d0ee5985c1a3 Mon Sep 17 00:00:00 2001 From: sahil-tgs Date: Wed, 17 Jul 2024 02:24:34 +0530 Subject: [PATCH 12/13] fix: Adjust component heights to prevent nav bar overlap --- frontend/src/basic/BookPage/index.tsx | 19 +++++++++---------- frontend/src/basic/SettingsPage/index.tsx | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/frontend/src/basic/BookPage/index.tsx b/frontend/src/basic/BookPage/index.tsx index ed74cc240..be33604a8 100644 --- a/frontend/src/basic/BookPage/index.tsx +++ b/frontend/src/basic/BookPage/index.tsx @@ -3,17 +3,16 @@ import { useTranslation } from 'react-i18next'; import { Button, Grid, ButtonGroup, Dialog, Box } from '@mui/material'; import { useNavigate } from 'react-router-dom'; import DepthChart from '../../components/Charts/DepthChart'; - import { NoRobotDialog } from '../../components/Dialogs'; import MakerForm from '../../components/MakerForm'; import BookTable from '../../components/BookTable'; // Icons import { BarChart, FormatListBulleted, Map } from '@mui/icons-material'; -import { AppContext, type UseAppStoreType } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; import MapChart from '../../components/Charts/MapChart'; -import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext'; -import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; +import { FederationContext, UseFederationStoreType } from '../../contexts/FederationContext'; +import { GarageContext, UseGarageStoreType } from '../../contexts/GarageContext'; const BookPage = (): JSX.Element => { const { windowSize } = useContext(AppContext); @@ -126,7 +125,7 @@ const BookPage = (): JSX.Element => { { @@ -153,19 +152,19 @@ const BookPage = (): JSX.Element => { ) : view === 'depth' ? ( ) : view === 'map' ? ( ) : ( { const { windowSize, navbarHeight } = useContext(AppContext); const { federation, addNewCoordinator } = useContext(FederationContext); - const maxHeight = (windowSize.height - navbarHeight) * 0.85 - 3; + const maxHeight = (windowSize.height * 0.65) const [newAlias, setNewAlias] = useState(''); const [newUrl, setNewUrl] = useState(''); const [error, setError] = useState(); From 739e6e1345c29337e51bbcbf2f408c08a7d001a9 Mon Sep 17 00:00:00 2001 From: sahil-tgs Date: Sat, 31 Aug 2024 04:26:49 +0530 Subject: [PATCH 13/13] ui-update 2 --- frontend/src/basic/RobotPage/RobotProfile.tsx | 612 ++++++++++-------- frontend/src/basic/RobotPage/index.tsx | 86 ++- frontend/src/basic/SettingsPage/index.tsx | 130 +++- .../src/components/FederationTable/index.tsx | 204 +++--- .../src/components/SettingsForm/index.tsx | 381 ++++++----- 5 files changed, 761 insertions(+), 652 deletions(-) diff --git a/frontend/src/basic/RobotPage/RobotProfile.tsx b/frontend/src/basic/RobotPage/RobotProfile.tsx index 4095d41a8..300288fbe 100644 --- a/frontend/src/basic/RobotPage/RobotProfile.tsx +++ b/frontend/src/basic/RobotPage/RobotProfile.tsx @@ -2,38 +2,45 @@ import React, { useState, useContext, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { - Button, - Grid, - LinearProgress, Typography, - Alert, + LinearProgress, Select, MenuItem, Box, + TextField, + SelectChangeEvent, useTheme, - Tooltip, - type SelectChangeEvent, + useMediaQuery, + styled, } from '@mui/material'; -import { Bolt, Add, DeleteSweep, Logout, Download } from '@mui/icons-material'; +import { Bolt, Add, DeleteSweep, Logout, Download, FileCopy } from '@mui/icons-material'; import RobotAvatar from '../../components/RobotAvatar'; -import TokenInput from './TokenInput'; -import { type Slot, type Robot } from '../../models'; -import { AppContext, type UseAppStoreType } from '../../contexts/AppContext'; +import { AppContext, UseAppStoreType } from '../../contexts/AppContext'; import { genBase62Token } from '../../utils'; -import { LoadingButton } from '@mui/lab'; -import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; -import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext'; +import { GarageContext, UseGarageStoreType } from '../../contexts/GarageContext'; +import { FederationContext, UseFederationStoreType } from '../../contexts/FederationContext'; + +const BUTTON_COLORS = { + primary: '#2196f3', + secondary: '#9c27b0', + text: '#ffffff', + hoverPrimary: '#4dabf5', + hoverSecondary: '#af52bf', + activePrimary: '#1976d2', + activeSecondary: '#7b1fa2', + deleteHover: '#ff6666', +}; + +const COLORS = { + shadow: '#000000', +}; interface RobotProfileProps { - robot: Robot; - setRobot: (state: Robot) => void; - setView: (state: 'welcome' | 'onboarding' | 'recovery' | 'profile') => void; - getGenerateRobot: (token: string, slot?: number) => void; inputToken: string; + getGenerateRobot: (token: string) => void; + setInputToken: (token: string) => void; logoutRobot: () => void; - setInputToken: (state: string) => void; - width: number; - baseUrl: string; + setView: (view: string) => void; } const RobotProfile = ({ @@ -42,7 +49,6 @@ const RobotProfile = ({ setInputToken, logoutRobot, setView, - width, }: RobotProfileProps): JSX.Element => { const { windowSize } = useContext(AppContext); const { garage, robotUpdatedAt, orderUpdatedAt } = useContext(GarageContext); @@ -51,6 +57,7 @@ const RobotProfile = ({ const { t } = useTranslation(); const theme = useTheme(); const navigate = useNavigate(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); const [loading, setLoading] = useState(true); @@ -82,278 +89,309 @@ const RobotProfile = ({ ).length; return ( - - - - {slot?.nickname ? ( - -
- {width < 19 ? null : ( - - )} - {slot?.nickname} - {width < 19 ? null : ( - - )} -
- - ) : ( - <> - {t('Building your robot!')} - - - )} - - - - + + + + {slot?.nickname} + + + + + {loadingCoordinators > 0 && !robot?.activeOrderId ? t('Looking for orders!') : t('Ready to Trade')} + + {loadingCoordinators > 0 && !robot?.activeOrderId && } + + { + logoutRobot(); + setView('welcome'); + }}> + + + navigator.clipboard.writeText(inputToken)}> + + + ), }} - tooltip={t('This is your trading avatar')} - tooltipPosition='top' /> - {robot?.found && Boolean(slot?.lastShortAlias) ? ( - - {t('Welcome back!')} - + + + + + + + {t('Robot Garage')} + + + + {loading ? ( + + {t('Building...')} + ) : ( - <> + Object.values(garage.slots).map((slot: Slot, index: number) => ( + + + + {slot?.nickname} + + + )) )} - - - {loadingCoordinators > 0 && !robot?.activeOrderId ? ( - - {t('Looking for orders!')} - - - ) : null} - - {Boolean(robot?.activeOrderId) && Boolean(slot?.hashId) ? ( - - - - ) : null} - - {Boolean(robot?.lastOrderId) && Boolean(slot?.hashId) ? ( - - - - - - - - - {t( - 'Reusing trading identity degrades your privacy against other users, coordinators and observers.', - )} - - - - - - - - - ) : null} - - {!robot?.activeOrderId && - slot?.hashId && - !robot?.lastOrderId && - loadingCoordinators === 0 ? ( - {t('No existing orders found')} - ) : null} - - - {t('DOWNLOAD')} + + )} + { + garage.delete(); + logoutRobot(); + setView('welcome'); + }} > - - - - - - null} - /> - - - - - - - - {t('Robot Garage')} - - - - - - -
- {t('Add Robot')} - - - - {window.NativeRobosats === undefined ? ( - - - - ) : null} - - - - - - - - - + {t('DELETE GARAGE')} + + + + ); }; -export default RobotProfile; +// Styled components +const ProfileContainer = styled(Box)<{ $isMobile: boolean }>(({ theme, $isMobile }) => ({ + width: '100%', + maxWidth: $isMobile ? '100%' : 1000, + margin: '0 auto', + display: 'flex', + flexDirection: $isMobile ? 'column' : 'row', + border: $isMobile ? '1px solid #000' : '2px solid #000', + borderRadius: '8px', + overflow: 'hidden', + boxShadow: $isMobile ? '4px 4px 0px #000000' : '8px 8px 0px #000000', +})); + +const InfoSection = styled(Box)<{ colors: typeof COLORS; $isMobile: boolean }>(({ theme, colors, $isMobile }) => ({ + flexGrow: 1, + flexBasis: $isMobile ? 'auto' : 0, + backgroundColor: theme.palette.background.paper, + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + padding: $isMobile ? theme.spacing(2) : theme.spacing(4), + borderBottom: $isMobile ? '1px solid #000' : 'none', + borderRight: $isMobile ? 'none' : '2px solid #000', +})); + +const NicknameTypography = styled(Typography)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + display: 'flex', + alignItems: 'center', + marginBottom: $isMobile ? '8px' : '16px', +})); + +const BoltIcon = styled(Bolt)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + color: '#fcba03', + height: $isMobile ? '0.8em' : '1em', + width: $isMobile ? '0.8em' : '1em', +})); + +const StyledRobotAvatar = styled(RobotAvatar)({ + '& img': { + border: '2px solid #555', + borderRadius: '50%', + }, +}); + +const StatusTypography = styled(Typography)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + marginBottom: $isMobile ? '8px' : '16px', +})); + +const StyledLinearProgress = styled(LinearProgress)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + width: '100%', + marginBottom: $isMobile ? '8px' : '16px', +})); + +const TokenBox = styled(Box)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + display: 'flex', + alignItems: 'center', + width: '100%', + border: $isMobile ? '1px solid #000' : '2px solid #000', + borderRadius: '4px', +})); + +const StyledTextField = styled(TextField)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + '& .MuiInputBase-root': { + height: $isMobile ? '36px' : '48px', + padding: $isMobile ? '2px 4px' : '4px 8px', + fontSize: $isMobile ? '0.8rem' : '1rem', + } +})); + +const RightSection = styled(Box)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + flexGrow: 1, + flexBasis: $isMobile ? 'auto' : 0, + display: 'flex', + flexDirection: 'column', + overflow: 'hidden', +})); + +const TitleSection = styled(Box)(({ theme }) => ({ + padding: theme.spacing(2), + borderBottom: `2px solid #000`, +})); + +const TitleTypography = styled(Typography)({ + fontWeight: 'bold', +}); + +const StyledSelect = styled(Select)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + width: '100%', + height: $isMobile ? '50px' : '80px', + borderBottom: $isMobile ? '1px solid #000' : '2px solid #000', + borderRadius: 0, + '& .MuiOutlinedInput-notchedOutline': { + border: 'none', + }, +})); + +const StyledMenuItem = styled(MenuItem)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + height: $isMobile ? '50px' : '80px', +})); + +const MenuItemContent = styled(Box)({ + display: 'flex', + alignItems: 'center', +}); + +const StyledMenuItemAvatar = styled(RobotAvatar)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + width: $isMobile ? '24px' : '30px', + height: $isMobile ? '24px' : '30px', + marginRight: '8px', +})); + +const ButtonContainer = styled(Box)({ + display: 'flex', + flexDirection: 'column', + width: '100%', +}); + +const StyledButton = styled('button')<{ + $buttonColor: string; + $hoverColor: string; + $textColor: string; + $isMobile: boolean; +}>(({ theme, $buttonColor, $hoverColor, $textColor, $isMobile }) => ({ + justifyContent: 'center', + alignItems: 'center', + textAlign: 'center', + padding: theme.spacing(2), + border: 'none', + borderRadius: 0, + backgroundColor: $buttonColor, + color: $textColor, + cursor: 'pointer', + display: 'flex', + transition: 'background-color 0.3s ease, color 0.3s ease', + width: '100%', + height: $isMobile ? '40px' : '60px', + borderBottom: $isMobile ? '1px solid #000' : '2px solid #000', + '&:hover': { + backgroundColor: $hoverColor, + color: $buttonColor === 'transparent' ? '#fff' : $textColor, + }, + '&:active': { + backgroundColor: $buttonColor === BUTTON_COLORS.primary ? BUTTON_COLORS.activePrimary : BUTTON_COLORS.activeSecondary, + }, + '&:focus': { + outline: 'none', + }, +})); + +const CustomIconButton = styled('button')({ + background: 'transparent', + border: 'none', + color: '#1976d2', + padding: '4px', + cursor: 'pointer', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + transition: 'color 0.3s ease', + '&:hover': { + color: '#0d47a1', + }, + '&:active': { + color: '#002171', + }, +}); + +const StyledLogoutIcon = styled(Logout)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + fontSize: $isMobile ? '1rem' : '1.5rem', +})); + +const StyledFileCopyIcon = styled(FileCopy)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + fontSize: $isMobile ? '1rem' : '1.5rem', +})); + +const StyledAddIcon = styled(Add)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + marginRight: '8px', + fontSize: $isMobile ? '1rem' : '1.5rem', +})); + +const StyledDownloadIcon = styled(Download)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + marginRight: '8px', + fontSize: $isMobile ? '1rem' : '1.5rem', +})); + +const StyledDeleteSweepIcon = styled(DeleteSweep)<{ $isMobile: boolean }>(({ $isMobile }) => ({ + marginRight: '8px', + fontSize: $isMobile ? '1rem' : '1.5rem', +})); + +export default RobotProfile; \ No newline at end of file diff --git a/frontend/src/basic/RobotPage/index.tsx b/frontend/src/basic/RobotPage/index.tsx index 5015c849e..c0b4c9931 100644 --- a/frontend/src/basic/RobotPage/index.tsx +++ b/frontend/src/basic/RobotPage/index.tsx @@ -1,7 +1,6 @@ import React, { useContext, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { - Paper, Grid, CircularProgress, Box, @@ -129,42 +128,40 @@ const RobotPage = (): JSX.Element => { } else { return ( - - {view === 'welcome' && ( - - )} - - {view === 'onboarding' && ( - - )} - - {view === 'profile' && ( - - )} - - {view === 'recovery' && ( - - )} - + {view === 'welcome' && ( + + )} + + {view === 'onboarding' && ( + + )} + + {view === 'profile' && ( + + )} + + {view === 'recovery' && ( + + )} ); } @@ -174,11 +171,13 @@ const RobotPage = (): JSX.Element => { const StyledConnectingBox = styled(Box)({ width: '100vw', height: 'auto', + backgroundColor: 'transparent', }); const StyledTorIconBox = styled(Box)({ position: 'fixed', top: '4.6em', + backgroundColor: 'transparent', }); const StyledMainBox = styled(Box)({ @@ -188,18 +187,9 @@ const StyledMainBox = styled(Box)({ justifyContent: 'center', alignItems: 'center', padding: '2em', -}); - -const StyledPaper = styled(Paper)(({ theme }) => ({ - width: '80vw', - maxWidth: '1200px', - maxHeight: '85vh', - overflow: 'auto', - overflowX: 'clip', backgroundColor: 'transparent', border: 'none', - boxShadow: 'none', - padding: '1em', -})); + boxShadow: 'none', +}); export default RobotPage; diff --git a/frontend/src/basic/SettingsPage/index.tsx b/frontend/src/basic/SettingsPage/index.tsx index b6be6be0a..6bcf23621 100644 --- a/frontend/src/basic/SettingsPage/index.tsx +++ b/frontend/src/basic/SettingsPage/index.tsx @@ -1,19 +1,20 @@ import React, { useContext, useState } from 'react'; -import { Button, Grid, List, ListItem, Paper, TextField, Typography } from '@mui/material'; +import { Button, Grid, Paper, TextField, Typography, Box } from '@mui/material'; import SettingsForm from '../../components/SettingsForm'; import { AppContext, type UseAppStoreType } from '../../contexts/AppContext'; import FederationTable from '../../components/FederationTable'; import { t } from 'i18next'; import { FederationContext, type UseFederationStoreType } from '../../contexts/FederationContext'; +import { styled } from '@mui/system'; const SettingsPage = (): JSX.Element => { const { windowSize, navbarHeight } = useContext(AppContext); const { federation, addNewCoordinator } = useContext(FederationContext); - const maxHeight = (windowSize.height * 0.65) + const maxHeight = (windowSize.height * 0.65); const [newAlias, setNewAlias] = useState(''); const [newUrl, setNewUrl] = useState(''); const [error, setError] = useState(); - // Regular expression to match a valid .onion URL + const onionUrlPattern = /^((http|https):\/\/)?[a-zA-Z2-7]{16,56}\.onion$/; const addCoordinator: () => void = () => { @@ -35,31 +36,22 @@ const SettingsPage = (): JSX.Element => { }; return ( - + - + - - - - - - - {error} - - - - - + + + + + {error && ( + + {error} + + )} + + { setNewAlias(e.target.value); }} /> - { setNewUrl(e.target.value); }} /> - - - + + + - + ); }; -export default SettingsPage; +// Styled Components +const SettingsContainer = styled(Paper)(({ theme }) => ({ + display: 'flex', + width: '80vw', + height: '70vh', + margin: '0 auto', + border: '2px solid #000', + borderRadius: '8px', + overflow: 'hidden', + boxShadow: '8px 8px 0px #000', + [theme.breakpoints.down('md')]: { + flexDirection: 'column', + width: '90vw', + height: 'fit-content', + marginTop: '30rem', + }, +})); + +const LeftGrid = styled(Grid)(({ theme }) => ({ + padding: '2rem', + borderRight: '2px solid #000', + [theme.breakpoints.down('md')]: { + padding: '1rem', + borderRight: 'none', + }, +})); + +const RightGrid = styled(Grid)(({ theme }) => ({ + padding: '2rem', + display: 'flex', + flexDirection: 'column', + [theme.breakpoints.down('md')]: { + padding: '1rem', + }, +})); + +const FederationTableWrapper = styled(Box)({ + flexGrow: 1, + '& > *': { + width: '100% !important', + }, +}); + +const ErrorTypography = styled(Typography)({ + // You can add specific styles for the error message here if needed +}); + +const InputContainer = styled(Box)(({ theme }) => ({ + display: 'flex', + flexDirection: 'row', + gap: theme.spacing(1), + marginTop: theme.spacing(2), + [theme.breakpoints.down('sm')]: { + flexDirection: 'column', + }, +})); + +const StyledTextField = styled(TextField)(({ theme }) => ({ + flexGrow: 1, + [theme.breakpoints.down('sm')]: { + marginBottom: theme.spacing(2), + }, +})); + +const StyledButton = styled(Button)(({ theme }) => ({ + maxHeight: 40, + [theme.breakpoints.down('sm')]: { + width: '100%', + }, +})); + +export default SettingsPage; \ No newline at end of file diff --git a/frontend/src/components/FederationTable/index.tsx b/frontend/src/components/FederationTable/index.tsx index 9e64483bb..8851230ba 100644 --- a/frontend/src/components/FederationTable/index.tsx +++ b/frontend/src/components/FederationTable/index.tsx @@ -1,78 +1,38 @@ -import React, { useCallback, useEffect, useState, useContext, useMemo } from 'react'; +import React, { useCallback, useContext, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { Box, useTheme, Checkbox, CircularProgress, Typography, Grid } from '@mui/material'; +import { Box, Checkbox, CircularProgress, Grid, Typography } from '@mui/material'; import { DataGrid, type GridColDef, type GridValidRowModel } from '@mui/x-data-grid'; import { type Coordinator } from '../../models'; import RobotAvatar from '../RobotAvatar'; import { Link, LinkOff } from '@mui/icons-material'; import { AppContext, type UseAppStoreType } from '../../contexts/AppContext'; import { type UseFederationStoreType, FederationContext } from '../../contexts/FederationContext'; -import headerStyleFix from '../DataGrid/HeaderFix'; +import { styled } from '@mui/system'; interface FederationTableProps { maxWidth?: number; - maxHeight?: number; fillContainer?: boolean; } -const FederationTable = ({ - maxWidth = 90, - maxHeight = 50, - fillContainer = false, -}: FederationTableProps): JSX.Element => { +const FederationTable = ({ maxWidth = 90, fillContainer = false }: FederationTableProps): JSX.Element => { const { t } = useTranslation(); - const { federation, sortedCoordinators, coordinatorUpdatedAt, federationUpdatedAt } = - useContext(FederationContext); + const { federation, sortedCoordinators, coordinatorUpdatedAt } = useContext(FederationContext); const { setOpen, settings } = useContext(AppContext); - const theme = useTheme(); - const [pageSize, setPageSize] = useState(0); - - // all sizes in 'em' - const fontSize = theme.typography.fontSize; - const verticalHeightFrame = 3.3; - const verticalHeightRow = 3.27; - const defaultPageSize = Math.max( - Math.floor((maxHeight - verticalHeightFrame) / verticalHeightRow), - 1, - ); - const height = defaultPageSize * verticalHeightRow + verticalHeightFrame; - - const [useDefaultPageSize, setUseDefaultPageSize] = useState(true); - - useEffect(() => { - if (useDefaultPageSize) { - setPageSize(defaultPageSize); - } - }, [coordinatorUpdatedAt, federationUpdatedAt]); - - const localeText = { - MuiTablePagination: { labelRowsPerPage: t('Coordinators per page:') }, - noResultsOverlayLabel: t('No coordinators found.'), - }; - - const onClickCoordinator = function (shortAlias: string): void { - setOpen((open) => { - return { ...open, coordinator: shortAlias }; - }); - }; const aliasObj = useCallback((width: number) => { return { field: 'longAlias', headerName: t('Coordinator'), - width: width * fontSize, + width: width, renderCell: (params: any) => { const coordinator = federation.coordinators[params.row.shortAlias]; return ( - { - onClickCoordinator(params.row.shortAlias); - }} - alignItems='center' + direction="row" + wrap="nowrap" + onClick={() => onClickCoordinator(params.row.shortAlias)} + alignItems="center" spacing={1} > @@ -87,31 +47,29 @@ const FederationTable = ({ {params.row.longAlias} - + ); }, }; - }, []); + }, [federation.coordinators]); const enabledObj = useCallback( (width: number) => { return { field: 'enabled', headerName: t('Enabled'), - width: width * fontSize, + width: width, renderCell: (params: any) => { return ( { - onEnableChange(params.row.shortAlias); - }} + onClick={() => onEnableChange(params.row.shortAlias)} /> ); }, }; }, - [coordinatorUpdatedAt], + [coordinatorUpdatedAt] ); const upObj = useCallback( @@ -119,52 +77,41 @@ const FederationTable = ({ return { field: 'up', headerName: t('Up'), - width: width * fontSize, + width: width, renderCell: (params: any) => { return ( -
{ - onClickCoordinator(params.row.shortAlias); - }} - > - {Boolean(params.row.loadingInfo) && Boolean(params.row.enabled) ? ( - + onClickCoordinator(params.row.shortAlias)}> + {params.row.loadingInfo && params.row.enabled ? ( + ) : params.row.info !== undefined ? ( - + ) : ( - + )} -
+ ); }, }; }, - [coordinatorUpdatedAt], + [coordinatorUpdatedAt] ); const columnSpecs = { alias: { - priority: 2, - order: 1, normal: { - width: 12.1, + width: 200, object: aliasObj, }, }, up: { - priority: 3, - order: 2, normal: { - width: 3.5, + width: 70, object: upObj, }, }, enabled: { - priority: 1, - order: 3, normal: { - width: 5, + width: 90, object: enabledObj, }, }, @@ -174,28 +121,16 @@ const FederationTable = ({ columns: Array>; width: number; } { - const useSmall = maxWidth < 30; const selectedColumns: object[] = []; let width: number = 0; for (const value of Object.values(columnSpecs)) { - const colWidth = Number( - useSmall && Boolean(value.small) ? value.small.width : value.normal.width, - ); - const colObject = useSmall && Boolean(value.small) ? value.small.object : value.normal.object; - - if (width + colWidth < maxWidth || selectedColumns.length < 2) { - width = width + colWidth; - selectedColumns.push([colObject(colWidth, false), value.order]); - } else { - selectedColumns.push([colObject(colWidth, true), value.order]); - } - } + const colWidth = value.normal.width; + const colObject = value.normal.object; - // sort columns by column.order value - selectedColumns.sort(function (first, second) { - return first[1] - second[1]; - }); + width += colWidth; + selectedColumns.push([colObject(colWidth), value.order]); + } const columns: Array> = selectedColumns.map(function (item) { return item[0]; @@ -204,7 +139,7 @@ const FederationTable = ({ return { columns, width: width * 0.9 }; }; - const { columns, width } = filteredColumns(); + const { columns } = filteredColumns(); const onEnableChange = function (shortAlias: string): void { if (federation.getCoordinator(shortAlias).enabled === true) { @@ -217,38 +152,65 @@ const FederationTable = ({ const reorderedCoordinators = useMemo(() => { return sortedCoordinators.reduce((coordinators, key) => { coordinators[key] = federation.coordinators[key]; - return coordinators; }, {}); - }, [settings.network, federationUpdatedAt]); + }, [settings.network, coordinatorUpdatedAt]); + + const onClickCoordinator = (shortAlias: string): void => { + setOpen((open) => { + return { ...open, coordinator: shortAlias }; + }); + }; return ( - - + params.shortAlias} columns={columns} checkboxSelection={false} - pageSize={pageSize} - rowsPerPageOptions={width < 22 ? [] : [0, pageSize, defaultPageSize * 2, 50, 100]} - onPageSizeChange={(newPageSize) => { - setPageSize(newPageSize); - setUseDefaultPageSize(false); - }} - hideFooter={true} + autoHeight + hideFooter /> - + ); }; -export default FederationTable; +// Styled Components +const TableContainer = styled(Box, { + shouldForwardProp: (prop) => prop !== 'fillContainer' && prop !== 'maxWidth', +})<{ fillContainer: boolean; maxWidth: number }>(({ theme, fillContainer, maxWidth }) => ({ + width: fillContainer ? '100%' : `${maxWidth}em`, + height: 'fit-content', + border: '2px solid black', + overflow: 'hidden', + borderRadius: '8px', + boxShadow: 'none', +})); + +const StyledDataGrid = styled(DataGrid)(({ theme }) => ({ + '& .MuiDataGrid-root': { + fontSize: { xs: '0.8rem', sm: '0.9rem', md: '1rem' }, + }, + '& .MuiDataGrid-cell': { + padding: { xs: '0.5rem', sm: '1rem' }, + }, + '& .MuiDataGrid-columnHeaders': { + backgroundColor: theme.palette.background.default, + borderBottom: '2px solid black', + fontSize: { xs: '0.8rem', sm: '0.9rem', md: '1rem' }, + }, +})); + +const CoordinatorGrid = styled(Grid)({ + cursor: 'pointer', + position: 'relative', + left: '-0.3em', + width: '50em', +}); + +const UpStatusContainer = styled('div')({ + cursor: 'pointer', +}); + +export default FederationTable; \ No newline at end of file diff --git a/frontend/src/components/SettingsForm/index.tsx b/frontend/src/components/SettingsForm/index.tsx index 76f994b0b..d7292798c 100644 --- a/frontend/src/components/SettingsForm/index.tsx +++ b/frontend/src/components/SettingsForm/index.tsx @@ -1,36 +1,36 @@ import React, { useContext } from 'react'; import { useTranslation } from 'react-i18next'; +import { useTheme } from '@mui/material/styles'; import { type UseAppStoreType, AppContext } from '../../contexts/AppContext'; import { Grid, - Paper, Switch, - useTheme, FormControlLabel, List, ListItem, ListItemIcon, - Slider, Typography, ToggleButtonGroup, ToggleButton, + Box, + Slider, } from '@mui/material'; import SelectLanguage from './SelectLanguage'; import { Translate, Palette, - LightMode, - DarkMode, SettingsOverscan, Link, AccountBalance, AttachMoney, QrCode, + DarkMode, } from '@mui/icons-material'; import { systemClient } from '../../services/System'; import { TorIcon } from '../Icons'; import SwapCalls from '@mui/icons-material/SwapCalls'; import { apiClient } from '../../services/api'; +import { styled } from '@mui/system'; interface SettingsFormProps { dense?: boolean; @@ -38,8 +38,9 @@ interface SettingsFormProps { const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => { const { fav, setFav, settings, setSettings } = useContext(AppContext); - const theme = useTheme(); const { t } = useTranslation(); + const theme = useTheme(); + const fontSizes = [ { label: 'XS', value: { basic: 12, pro: 10 } }, { label: 'S', value: { basic: 13, pro: 11 } }, @@ -48,115 +49,74 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => { { label: 'XL', value: { basic: 16, pro: 14 } }, ]; + const handleToggleChange = (e, newValue) => { + if (newValue !== null) { + setFav({ ...fav, mode: newValue, currency: newValue === 'fiat' ? 0 : 1000 }); + } + }; + + const handleNetworkChange = (e, newValue) => { + if (newValue !== null) { + setSettings({ ...settings, network: newValue }); + systemClient.setItem('settings_network', newValue); + } + }; + return ( - - + + - - - - - + + + + + + {t('Language Settings')} + + + { setSettings({ ...settings, language }); systemClient.setItem('settings_language', language); }} /> - + - - - - - - - - } - icon={ - - - - } - onChange={(e) => { - const mode = e.target.checked ? 'dark' : 'light'; - setSettings({ ...settings, mode }); - systemClient.setItem('settings_mode', mode); - }} - /> - } - /> - {settings.mode === 'dark' ? ( - <> - - - - + + + + + + {t('Appearance Settings')} + + + + { + const mode = e.target.checked ? 'dark' : 'light'; + setSettings({ ...settings, mode }); + systemClient.setItem('settings_mode', mode); + }} + /> + } + /> + {settings.mode === 'dark' && ( + - - - } - icon={ - - - - } onChange={(e) => { const lightQRs = !e.target.checked; setSettings({ ...settings, lightQRs }); @@ -165,17 +125,21 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => { /> } /> - - ) : ( - <> - )} - + )} + + - - - - - + + + + + + {t('Font Size')} + + + { setSettings({ ...settings, fontSize }); systemClient.setItem(`settings_fontsize_${settings.frontend}`, fontSize.toString()); }} - valueLabelDisplay='off' + valueLabelDisplay="off" + track={false} marks={fontSizes.map(({ label, value }) => ({ - label: {t(label)}, + label: {t(label)}, value: settings.frontend === 'basic' ? value.basic : value.pro, }))} - track={false} /> - + - - - - - + + + + + + {t('Currency Settings')} + + + { - setFav({ ...fav, mode, currency: mode === 'fiat' ? 0 : 1000 }); - }} + onChange={handleToggleChange} + fullWidth > - + {t('Fiat')} - - + + {t('Swaps')} - - - + + + - - - - - + + + + + + {t('Network Settings')} + + + { - setSettings({ ...settings, network }); - systemClient.setItem('settings_network', network); - }} + onChange={handleNetworkChange} + fullWidth > - + {t('Mainnet')} - - + + {t('Testnet')} - - - + + + + {/* Proxy Settings */} {window.NativeRobosats !== undefined && ( - - - - - + + + + + + {t('Proxy Settings')} + + + { - setSettings({ ...settings, useProxy }); - systemClient.setItem('settings_use_proxy', String(useProxy)); - apiClient.useProxy = useProxy; + if (useProxy !== null) { + setSettings({ ...settings, useProxy }); + systemClient.setItem('settings_use_proxy', String(useProxy)); + apiClient.useProxy = useProxy; + } }} + fullWidth > - - {t('Build-in')} - - + + {t('Built-in')} + + {t('Disabled')} - - - + + + )} @@ -266,4 +248,79 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): JSX.Element => { ); }; -export default SettingsForm; +// Styled Components +const StyledListItem = styled(ListItem)({ + display: 'block', +}); + +const SettingHeader = styled(Box)({ + display: 'flex', + alignItems: 'center', + marginBottom: '0.5em', +}); + +const StyledSelectLanguage = styled(SelectLanguage)(({ theme }) => ({ + '& .MuiOutlinedInput-root': { + borderRadius: '8px', + border: '2px solid black', + boxShadow: '4px 4px 0px rgba(0, 0, 0, 1)', + width: '100%', + }, +})); + +const AppearanceSettingsBox = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + flexDirection: 'column', + gap: theme.spacing(2), + [theme.breakpoints.up('sm')]: { + flexDirection: 'row', + gap: theme.spacing(1), + }, +})); + +const QRCodeSwitch = styled(FormControlLabel)(({ theme }) => ({ + marginLeft: 0, + [theme.breakpoints.up('sm')]: { + marginLeft: theme.spacing(2), + }, +})); + +const StyledSlider = styled(Slider)(({ theme }) => ({ + '& .MuiSlider-thumb': { + borderRadius: '8px', + border: '2px solid black', + }, + '& .MuiSlider-track': { + borderRadius: '8px', + border: '2px solid black', + }, + '& .MuiSlider-rail': { + borderRadius: '8px', + border: '2px solid black', + }, +})); + +const StyledToggleButtonGroup = styled(ToggleButtonGroup)({ + width: '100%', +}); + +const StyledToggleButton = styled(ToggleButton)(({ theme }) => ({ + borderRadius: '8px', + border: '2px solid black', + boxShadow: 'none', + fontWeight: 'bold', + width: '100%', + '&.Mui-selected': { + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText, + '&:hover': { + backgroundColor: theme.palette.primary.main, + }, + }, + '&:hover': { + backgroundColor: 'initial', + }, +})); + +export default SettingsForm; \ No newline at end of file