Introduce mobile design (#130)

This commit is contained in:
Maria Rushkova 2021-07-19 13:03:41 +02:00 committed by GitHub
parent e24e34fd04
commit a3caacd55a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 427 additions and 128 deletions

View File

@ -1,7 +1,6 @@
import React from 'react'
import React, { useEffect, useState } from 'react'
import { Redirect, Route, Switch } from 'react-router'
import { BrowserRouter } from 'react-router-dom'
import styled from 'styled-components'
import { GlobalStyle } from './providers/GlobalStyle'
import { TopBar } from './components/top/TopBar'
@ -9,25 +8,70 @@ import { Votes } from './pages/Votes'
import { Directory } from './pages/Directory'
import { Info } from './pages/Info'
import { NotificationsList } from './components/NotificationsList'
import { VotesMobile } from './pages/VotesMobile'
import { DirectoryMobile } from './pages/DirectoryMobile'
import { InfoMobile } from './pages/InfoMobile'
export function App() {
return (
<Page>
<BrowserRouter>
<GlobalStyle />
<TopBar />
<PageContent>
<Switch>
<Route exact path="/votes" component={Votes} />
<Route exact path="/directory" component={Directory} />
<Route exact path="/info" component={Info} />
</Switch>
<Redirect exact from="/" to="/votes" />
</PageContent>
</BrowserRouter>
<NotificationsList />
</Page>
)
const [mobileVersion, setMobileVersion] = useState(false)
useEffect(() => {
if (window.innerWidth < 600) {
setMobileVersion(true)
} else {
setMobileVersion(false)
}
}, [])
useEffect(() => {
const handleResize = () => {
if (window.innerWidth < 600) {
setMobileVersion(true)
} else {
setMobileVersion(false)
}
}
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
}, [window.innerWidth])
if (mobileVersion) {
return (
<Page>
<BrowserRouter>
<GlobalStyle />
<PageContentMobile>
<Switch>
<Route exact path="/votes" component={VotesMobile} />
<Route exact path="/directory" component={DirectoryMobile} />
<Route exact path="/info" component={InfoMobile} />
</Switch>
<Redirect exact from="/" to="/votes" />
</PageContentMobile>
</BrowserRouter>
<NotificationsList />
</Page>
)
} else {
return (
<Page>
<BrowserRouter>
<GlobalStyle />
<TopBar />
<PageContent>
<Switch>
<Route exact path="/votes" component={Votes} />
<Route exact path="/directory" component={Directory} />
<Route exact path="/info" component={Info} />
</Switch>
<Redirect exact from="/" to="/votes" />
</PageContent>
</BrowserRouter>
<NotificationsList />
</Page>
)
}
}
const Page = styled.div`
@ -40,3 +84,8 @@ const PageContent = styled.div`
margin: 0 auto;
position: relative;
`
const PageContentMobile = styled.div`
height: 100%;
padding: 196px 16px 16px;
position: relative;
`

View File

@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.25 8C7.25 7.58579 7.58579 7.25 8 7.25H10.5C10.9142 7.25 11.25 7.58579 11.25 8V15.5C11.25 15.9142 10.9142 16.25 10.5 16.25C10.0858 16.25 9.75 15.9142 9.75 15.5V8.75H8C7.58579 8.75 7.25 8.41421 7.25 8Z" fill="#8C21BA"/>
<path d="M10 5.75C10.6904 5.75 11.25 5.19036 11.25 4.5C11.25 3.80964 10.6904 3.25 10 3.25C9.30964 3.25 8.75 3.80964 8.75 4.5C8.75 5.19036 9.30964 5.75 10 5.75Z" fill="#8C21BA"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 10C0 15.5228 4.47715 20 10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10ZM1.5 10C1.5 14.6944 5.30558 18.5 10 18.5C14.6944 18.5 18.5 14.6944 18.5 10C18.5 5.30558 14.6944 1.5 10 1.5C5.30558 1.5 1.5 5.30558 1.5 10Z" fill="#8C21BA"/>
</svg>

After

Width:  |  Height:  |  Size: 829 B

View File

@ -27,6 +27,11 @@ const InfoHeading = styled.h1`
letter-spacing: -0.4px;
margin-bottom: 8px;
text-align: center;
@media (max-width: 600px) {
font-size: 22px;
line-height: 30px;
}
`
const InfoText = styled.p`
@ -34,4 +39,10 @@ const InfoText = styled.p`
text-align: center;
line-height: 32px;
margin-bottom: 24px;
@media (max-width: 600px) {
font-size: 13px;
line-height: 18px;
margin: 0 21px 8px 21px;
}
`

View File

@ -0,0 +1,121 @@
import React from 'react'
import styled from 'styled-components'
import { Colors } from '../constants/styles'
export function Rules() {
return (
<VotingRules>
<RulesHeading>Voting rules</RulesHeading>
<Rule>
<RuleIcon>🗳</RuleIcon>
<RuleText>
SNT holders can use their SNT voting power to vote for or against addition or removal of communities in the
directory.
</RuleText>
</Rule>
<Rule>
<RuleIcon>👍</RuleIcon>
<RuleText>
If a vote to add ends in favour of a community, the community will be added to the directory. Otherwise, a new
vote for the addition of that community can be started after 30 days.
</RuleText>
</Rule>
<Rule>
<RuleIcon>🗑</RuleIcon>
<RuleText>
If a vote to remove a community ends in favour of the removal, the community will disappear from the
directory. Otherwise, a new vote for removal can be submitted after about 30 days.
</RuleText>
</Rule>
<Rule>
<RuleIcon></RuleIcon>
<RuleText>
The minimum amount of SNT needed to start a new vote doubles with every failed vote attempt (for both addition
and removal votes).
</RuleText>
</Rule>
<Rule>
<RuleIcon></RuleIcon>
<RuleText>
Each vote lasts for two weeks. When the voting period ends, someone has to finalize the vote with a
finalization transaction.
</RuleText>
</Rule>
<Rule>
<RuleIcon></RuleIcon>
<RuleText>
If a single vote of more than 2,000,000 SNT votes for is made in favor of the removal of a community, the
remaining vote duration shortens to 24 hours. This shortening of the vote duration can be reversed if a single
vote of more than 2,000,000 SNT is made against the removal.
</RuleText>
</Rule>
<Rule>
<RuleIcon></RuleIcon>
<RuleText>
There is an always ongoing vote for which communities from the directory should appear in the Weekly Featured
section. Every week, five communities with the most votes are added to the section, replacing the five
communities that were featured the previous week. After the community leaves Weekly Featured, it cannot get
there again for three weeks.
</RuleText>
</Rule>
</VotingRules>
)
}
const VotingRules = styled.div`
max-width: 648px;
padding: 24px;
margin: 0 auto;
background-color: ${Colors.VioletSecondaryLight};
box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.1);
border-radius: 6px;
@media (max-width: 768px) {
max-width: 524px;
}
@media (max-width: 600px) {
padding: 0;
background-color: unset;
border: none;
box-shadow: none;
}
`
const RulesHeading = styled.p`
font-weight: bold;
font-size: 22px;
line-height: 24px;
margin-bottom: 24px;
@media (max-width: 600px) {
font-size: 17px;
}
`
const Rule = styled.div`
display: flex;
justify-content: space-between;
&:not(:last-child) {
margin-bottom: 16px;
}
`
const RuleIcon = styled.p`
font-size: 24px;
line-height: 30px;
margin-right: 16px;
}
`
const RuleText = styled.p`
line-height: 22px;
@media (max-width: 600px) {
font-size: 13px;
line-height: 18px;
`

View File

@ -66,7 +66,7 @@ export function TopBar() {
)
}
const Header = styled.header`
export const Header = styled.header`
position: fixed;
top: 0;
left: 0;
@ -138,7 +138,7 @@ const NavItem = styled.li`
}
`
const StyledNavLink = styled(NavLink)`
export const StyledNavLink = styled(NavLink)`
position: relative;
color: ${Colors.Black};
font-size: 17px;
@ -180,16 +180,20 @@ const StyledNavLink = styled(NavLink)`
}
`
const ButtonConnect = styled(ConnectButton)`
export const ButtonConnect = styled(ConnectButton)`
padding: 10px 27px;
width: auto;
@media (max-width: 600px) {
padding: 3px 27px;
}
`
const AccountWrap = styled.div`
export const AccountWrap = styled.div`
position: relative;
`
const Account = styled.button`
export const Account = styled.button`
position: relative;
font-weight: 500;
font-size: 13px;
@ -219,7 +223,7 @@ const Account = styled.button`
border-radius: 50%;
}
`
const ButtonDisconnect = styled.button`
export const ButtonDisconnect = styled.button`
position: absolute;
top: calc(100% + 4px);
right: 0;

View File

@ -0,0 +1,60 @@
import React, { useEffect, useState } from 'react'
import styled from 'styled-components'
import { useEthers, shortenAddress } from '@usedapp/core'
import infoIcon from '../assets/images/info.svg'
import { Colors } from '../constants/styles'
import { AccountWrap, Account, ButtonDisconnect, ButtonConnect, StyledNavLink } from '../components/top/TopBar'
export const ConnectMobile = () => {
const { account, deactivate } = useEthers()
const [isOpened, setIsOpened] = useState(false)
useEffect(() => {
window.addEventListener('click', () => setIsOpened(false))
return () => {
window.removeEventListener('click', () => setIsOpened(false))
}
}, [])
return (
<MenuContentMobile>
<StyledNavLinkInfo activeClassName="active-page" to="/info" />
{account ? (
<AccountWrap>
<Account
onClick={(e) => {
e.stopPropagation()
setIsOpened(!isOpened)
}}
>
{shortenAddress(account)}
</Account>
<ButtonDisconnect className={isOpened ? 'opened' : undefined} onClick={() => deactivate()}>
Disconnect
</ButtonDisconnect>
</AccountWrap>
) : (
<ButtonConnect text={'Connect'} />
)}
</MenuContentMobile>
)
}
const MenuContentMobile = styled.div`
display: flex;
align-items: flex-start;
justify-content: space-between;
margin: 16px;
background-color: ${Colors.GrayLight};
`
const StyledNavLinkInfo = styled(StyledNavLink)`
width: 20px;
height: 20px;
padding: 0;
background-image: url(${infoIcon});
background-repeat: no-repeat;
&.active-page::after {
width: 0;
}
`

View File

@ -0,0 +1,94 @@
import React from 'react'
import styled from 'styled-components'
import { Colors } from '../constants/styles'
import { Header, StyledNavLink } from '../components/top/TopBar'
import { PageInfo } from '../components/PageInfo'
import { ConnectMobile } from './ConnectMobile'
interface TopBarMobileProps {
heading: string
text: string
}
export const TopBarMobile = ({ heading, text }: TopBarMobileProps) => {
return (
<HeaderMobile>
<HeaderWrapperMobile>
<ConnectMobile />
<PageInfo heading={heading} text={text} />
<NavigationMobile>
<NavLinks>
<NavItemMobile>
<StyledNavLinkMobile activeClassName="active-page" to="/votes">
Votes
</StyledNavLinkMobile>
</NavItemMobile>
<NavItemMobile>
<StyledNavLinkMobile activeClassName="active-page" to="/directory">
Directory
</StyledNavLinkMobile>
</NavItemMobile>
</NavLinks>
</NavigationMobile>
</HeaderWrapperMobile>
</HeaderMobile>
)
}
const HeaderMobile = styled(Header)`
height: 186px;
@media (max-width: 340px) {
height: 205px;
}
`
const HeaderWrapperMobile = styled.div`
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
position: relative;
`
const NavigationMobile = styled.nav`
width: 100%;
height: 41px;
position: absolute;
bottom: 0;
`
const NavLinks = styled.ul`
display: flex;
justify-content: space-between;
height: 100%;
color: ${Colors.Black};
`
const NavItemMobile = styled.li`
width: 50%;
display: flex;
align-items: center;
`
const StyledNavLinkMobile = styled(StyledNavLink)`
width: 100%;
font-size: 15px;
line-height: 22px;
padding: 12px 0;
text-align: center;
&.active-page::after {
content: '';
width: 100%;
height: 2px;
position: absolute;
bottom: 2px;
left: 50%;
transform: translateX(-50%);
@media (max-width: 600px) {
width: 100%;
}
}
`

View File

@ -0,0 +1,16 @@
import React from 'react'
import { DirectoryCards } from '../components/directory/DirectoryCards'
import { TopBarMobile } from '../componentsMobile/TopBarMobile'
export function DirectoryMobile() {
return (
<div>
<TopBarMobile
heading="Current directory"
text="Vote on your favourite communities being included in
Weekly Featured Communities"
/>
<DirectoryCards />
</div>
)
}

View File

@ -1,7 +1,6 @@
import React from 'react'
import styled from 'styled-components'
import { Colors } from '../constants/styles'
import { InfoWrap, PageInfo } from '../components/PageInfo'
import { Rules } from '../components/Rules'
export function Info() {
return (
@ -12,107 +11,7 @@ export function Info() {
text="This DApp allows SNT holders determine which communities should be included in the Status Communities directory"
/>
</InfoWrap>
<VotingRules>
<RulesHeading>Voting rules</RulesHeading>
<Rule>
<RuleIcon>🗳</RuleIcon>
<RuleText>
SNT holders can use their SNT voting power to vote for or against addition or removal of communities in the
directory.
</RuleText>
</Rule>
<Rule>
<RuleIcon>👍</RuleIcon>
<RuleText>
If a vote to add ends in favour of a community, the community will be added to the directory. Otherwise, a
new vote for the addition of that community can be started after 30 days.
</RuleText>
</Rule>
<Rule>
<RuleIcon>🗑</RuleIcon>
<RuleText>
If a vote to remove a community ends in favour of the removal, the community will disappear from the
directory. Otherwise, a new vote for removal can be submitted after about 30 days.
</RuleText>
</Rule>
<Rule>
<RuleIcon></RuleIcon>
<RuleText>
The minimum amount of SNT needed to start a new vote doubles with every failed vote attempt (for both
addition and removal votes).
</RuleText>
</Rule>
<Rule>
<RuleIcon></RuleIcon>
<RuleText>
Each vote lasts for two weeks. When the voting period ends, someone has to finalize the vote with a
finalization transaction.
</RuleText>
</Rule>
<Rule>
<RuleIcon></RuleIcon>
<RuleText>
If a single vote of more than 2,000,000 SNT votes for is made in favor of the removal of a community, the
remaining vote duration shortens to 24 hours. This shortening of the vote duration can be reversed if a
single vote of more than 2,000,000 SNT is made against the removal.
</RuleText>
</Rule>
<Rule>
<RuleIcon></RuleIcon>
<RuleText>
There is an always ongoing vote for which communities from the directory should appear in the Weekly
Featured section. Every week, five communities with the most votes are added to the section, replacing the
five communities that were featured the previous week. After the community leaves Weekly Featured, it cannot
get there again for three weeks.
</RuleText>
</Rule>
</VotingRules>
<Rules />
</div>
)
}
const VotingRules = styled.div`
max-width: 648px;
padding: 24px;
margin: 0 auto;
background-color: ${Colors.VioletSecondaryLight};
box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.1);
border-radius: 6px;
@media (max-width: 768px) {
max-width: 524px;
}
@media (max-width: 600px) {
padding: 0;
background-color: unset;
border: none;
box-shadow: none;
}
`
const RulesHeading = styled.p`
font-weight: bold;
font-size: 22px;
line-height: 24px;
margin-bottom: 24px;
`
const Rule = styled.div`
display: flex;
justify-content: space-between;
&:not(:last-child) {
margin-bottom: 16px;
}
`
const RuleIcon = styled.p`
font-size: 24px;
line-height: 30px;
margin-right: 16px;
`
const RuleText = styled.p`
line-height: 22px;
`

View File

@ -0,0 +1,25 @@
import React from 'react'
import styled from 'styled-components'
import { PageInfo } from '../components/PageInfo'
import { Rules } from '../components/Rules'
import { Header } from '../components/top/TopBar'
import { ConnectMobile } from '../componentsMobile/ConnectMobile'
export function InfoMobile() {
return (
<div>
<InfoMobileWrap>
<ConnectMobile />
<PageInfo
heading="What is this DApp about?"
text="This DApp allows SNT holders determine which communities should be included in the Status Communities directory"
/>
</InfoMobileWrap>
<Rules />
</div>
)
}
const InfoMobileWrap = styled(Header)`
height: 166px;
`

View File

@ -0,0 +1,15 @@
import React from 'react'
import { VotingCards } from '../components/votes/VotingCards'
import { TopBarMobile } from '../componentsMobile/TopBarMobile'
export function VotesMobile() {
return (
<div>
<TopBarMobile
heading="Ongoing Votes"
text="Help curate the Status Communities directory by voting which communities should be included"
/>
<VotingCards />
</div>
)
}