feat: mobile layout

This commit is contained in:
bartosz-lipinski 2021-03-21 15:14:00 -05:00
parent bbeaf5c420
commit d4a7ebd19a
6 changed files with 162 additions and 79 deletions

View File

@ -1,13 +1,10 @@
import React from 'react'; import React from 'react';
import './../../App.less'; import './../../App.less';
import { Divider, Layout, Menu } from 'antd'; import { Breadcrumb, Layout } from 'antd';
import { Link, useLocation } from 'react-router-dom'; import { Link, useLocation } from 'react-router-dom';
import { LABELS } from '../../constants'; import { LABELS } from '../../constants';
import config from './../../../package.json';
import { contexts, components } from '@oyster/common'; import { contexts, components } from '@oyster/common';
import { NewProposalMenuItem } from '../../views/proposal/new';
import { RegisterGovernanceMenuItem } from '../../views/governance/register';
import { Content, Header } from 'antd/lib/layout/layout'; import { Content, Header } from 'antd/lib/layout/layout';
import Logo from './dark-horizontal-combined-rainbow.inline.svg'; import Logo from './dark-horizontal-combined-rainbow.inline.svg';
@ -18,16 +15,30 @@ export const AppLayout = React.memo((props: any) => {
const { env } = useConnectionConfig(); const { env } = useConnectionConfig();
const location = useLocation(); const location = useLocation();
const paths: { [key: string]: string } = { const breadcrumbNameMap: any = {
'/': '1', '/governance': 'Governance',
'/dashboard': '2', '/apps/1': 'Application1',
'/apps/2': 'Application2',
'/apps/1/detail': 'Detail',
'/apps/2/detail': 'Detail',
}; };
const current = const pathSnippets = location.pathname.split('/').filter(i => i);
[...Object.keys(paths)].find(key => location.pathname.startsWith(key)) || const extraBreadcrumbItems = pathSnippets.map((_, index) => {
''; const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
const defaultKey = paths[current] || '1'; return (
const theme = 'dark'; <Breadcrumb.Item key={url}>
<Link to={url}>{breadcrumbNameMap[url]}</Link>
</Breadcrumb.Item>
);
});
const breadcrumbItems = [
<Breadcrumb.Item key="home">
<Link to="/">Home</Link>
</Breadcrumb.Item>,
].concat(extraBreadcrumbItems);
// TODO: add breadcrumb
return ( return (
<div className="App"> <div className="App">
@ -46,7 +57,8 @@ export const AppLayout = React.memo((props: any) => {
useWalletBadge={true} useWalletBadge={true}
/> />
</Header> </Header>
<Content style={{ padding: '0 50px', flexDirection: 'column' }}> <Content>
{/* <Breadcrumb>{breadcrumbItems}</Breadcrumb> */}
{props.children} {props.children}
</Content> </Content>
</Layout> </Layout>

View File

@ -23,7 +23,7 @@ export function Routes() {
<Switch> <Switch>
<Route exact path="/" component={() => <HomeView />} /> <Route exact path="/" component={() => <HomeView />} />
<Route path="/proposal/:id" children={<ProposalView />} /> <Route path="/proposal/:id" children={<ProposalView />} />
<Route path="/proposals/:id" children={<ProposalsView />} /> <Route path="/governance/:id" children={<ProposalsView />} />
<Route <Route
exact exact

View File

@ -1,58 +1,92 @@
import { Col, List, Row } from 'antd'; import { Badge, Col, List, Row, Statistic } from 'antd';
import React, { useMemo, useState } from 'react'; import React, { useMemo, useState } from 'react';
import { useProposals } from '../../contexts/proposals'; import { useProposals } from '../../contexts/proposals';
import './style.less'; // Don't remove this line, it will break dark mode if you do due to weird transpiling conditions import './style.less'; // Don't remove this line, it will break dark mode if you do due to weird transpiling conditions
import { TokenIcon } from '@oyster/common'; import { TokenIcon, useConnectionConfig, useWallet } from '@oyster/common';
import { Background } from './../../components/Background'; import { Background } from './../../components/Background';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { RegisterGovernanceMenuItem } from '../governance/register'; import { RegisterGovernanceMenuItem } from '../governance/register';
import { TimelockStateStatus } from '../../models/timelock';
export const HomeView = () => { export const HomeView = () => {
const history = useHistory(); const history = useHistory();
const { configs } = useProposals(); const { configs, proposals } = useProposals();
const [page, setPage] = useState(0); const { connected } = useWallet();
const listData = useMemo(() => { const listData = useMemo(() => {
const newListData: any[] = []; const newListData: any[] = [];
Object.keys(configs).forEach(key => { Object.keys(configs).forEach(configKey => {
const config = configs[key]; const config = configs[configKey];
const mint = config.info.governanceMint.toBase58();
const proposalCount = Object.keys(proposals).reduce((acc, proposalKey) => {
let proposal = proposals[proposalKey];
if(proposal.info.config.toBase58() == configKey) {
acc.active = acc.active + (
proposal.info.state.status === TimelockStateStatus.Voting ||
proposal.info.state.status === TimelockStateStatus.Draft ? 1 : 0);
acc.total = acc.total +1;
}
return acc;
}, {
active: 0,
total: 0,
});
newListData.push({ newListData.push({
href: '/proposals/' + key, href: '/governance/' + configKey,
title: config.info.name, title: config.info.name,
badge: <TokenIcon mintAddress={config.info.governanceMint} size={40} />, badge: <Badge count={proposalCount.active}>
<TokenIcon mintAddress={mint} size={40} />
</Badge>,
proposalCount,
configKey,
config, config,
}); });
}); });
return newListData; return newListData;
}, [configs]); }, [configs, proposals]);
return ( return (
<Row> <>
<Col flex="auto"> <Background />
<Background /> <Row>
<div className="governance-title"> <Col flex="auto" xxl={15} xs={24} className="governance-container">
<h1>Governance</h1> <div className="governance-title">
<RegisterGovernanceMenuItem style={{ marginLeft: 'auto', marginRight: 0 }} /> <h1>Governance</h1>
</div> <RegisterGovernanceMenuItem style={{ marginLeft: 'auto', marginRight: 0 }} disabled={!connected} />
<List </div>
itemLayout="vertical" <List
size="large" itemLayout="vertical"
loading={listData.length === 0} size="large"
pagination={false} loading={listData.length === 0}
dataSource={listData} pagination={false}
renderItem={item => ( dataSource={listData}
<List.Item key={item.title} className="governance-item" onClick={() => history.push(item.href)}>
<List.Item.Meta renderItem={item => (
avatar={item.badge} <List.Item key={item.title} className="governance-item" onClick={() => history.push(item.href)}
title={item.title} extra={
description={item.description} <>
/> <Statistic title="Proposals" value={item.proposalCount.total} />
</List.Item> </>
)} }>
/> <List.Item.Meta
</Col> avatar={item.badge}
</Row> title={item.title}
description={item.description}
>
</List.Item.Meta>
</List.Item>
)}
/>
</Col>
</Row>
</>
); );
}; };

View File

@ -1,17 +1,23 @@
@import '~antd/dist/antd.dark.less'; @import '~antd/dist/antd.dark.less';
.governance-container {
margin-left: auto;
margin-right: auto;
position: relative;
}
.governance-title { .governance-title {
font-family: "FF Oxide Solid"; font-family: "FF Oxide Solid";
font-style: normal; font-style: normal;
font-weight: 300; font-weight: 300;
letter-spacing: 0px; letter-spacing: 0px;
position: absolute; position: absolute;
top: 240px; top: -40px;
left: calc(20% + 10px);
z-index: 2; z-index: 2;
display: flex; display: flex;
align-items: center; align-items: center;
right: calc(20% + 10px); width: 100%;
padding: 0px 10px;
h1 { h1 {
font-size: 28px; font-size: 28px;
@ -24,13 +30,32 @@
background-color: #202020; background-color: #202020;
padding: 0px !important; padding: 0px !important;
border-radius: 15px; border-radius: 15px;
margin-left: 20%;
margin-right: 20%;
:hover { .ant-badge-count {
cursor: pointer; top: 8px !important;
background-color: #252425; right: 12px !important;
border-radius: 15px; }
.ant-list-item-extra {
user-select: none;
pointer-events: none;
display: flex;
margin-left: 0px !important;
margin-right: auto;
.ant-statistic {
margin-top: 5px;
padding: 0px 20px;
.ant-statistic-content {
font-size: 20px;
}
}
}
.ant-list-item-main {
flex: 0 !important;
min-width: 250px;
} }
.ant-list-item-meta { .ant-list-item-meta {
@ -46,3 +71,9 @@
} }
} }
} }
.governance-item:hover {
cursor: pointer;
background-color: #252425;
border-radius: 15px;
}

View File

@ -2,10 +2,9 @@ import { Col, List, Row } from 'antd';
import React, { useMemo, useState } from 'react'; import React, { useMemo, useState } from 'react';
import { useConfig, useProposals } from '../../contexts/proposals'; import { useConfig, useProposals } from '../../contexts/proposals';
import './style.less'; // Don't remove this line, it will break dark mode if you do due to weird transpiling conditions import './style.less'; // Don't remove this line, it will break dark mode if you do due to weird transpiling conditions
import { StateBadge, StateBadgeRibbon } from '../../components/Proposal/StateBadge'; import { StateBadge } from '../../components/Proposal/StateBadge';
import { urlRegex } from '../proposal';
import { useHistory, useParams } from 'react-router-dom'; import { useHistory, useParams } from 'react-router-dom';
import { TokenIcon } from '@oyster/common'; import { TokenIcon, useConnectionConfig, useWallet } from '@oyster/common';
import { NewProposalMenuItem } from '../proposal/new'; import { NewProposalMenuItem } from '../proposal/new';
const PAGE_SIZE = 10; const PAGE_SIZE = 10;
@ -15,6 +14,13 @@ export const ProposalsView = () => {
const { proposals } = useProposals(); const { proposals } = useProposals();
const config = useConfig(id); const config = useConfig(id);
const [page, setPage] = useState(0); const [page, setPage] = useState(0);
const { tokenMap } = useConnectionConfig();
const { connected } = useWallet();
const token = tokenMap.get(config?.info.governanceMint.toBase58() || '') as any;
const tokenBackground = token?.extensions?.background || 'https://solana.com/static/8c151e179d2d7e80255bdae6563209f2/6833b/validators.webp';
const mint = config?.info.governanceMint.toBase58() || '';
const listData = useMemo(() => { const listData = useMemo(() => {
const newListData: any[] = []; const newListData: any[] = [];
@ -24,21 +30,12 @@ export const ProposalsView = () => {
return; return;
} }
newListData.push({ newListData.push({
href: '/proposal/' + key, href: '/proposal/' + key,
title: proposal.info.state.name, title: proposal.info.state.name,
badge: <TokenIcon mintAddress={config?.info.governanceMint} size={30} />, badge: <TokenIcon mintAddress={mint} size={30} />,
status: proposal.info.state.status, status: proposal.info.state.status,
proposal, proposal,
description: proposal.info.state.descLink.match(urlRegex) ? (
<a href={proposal.info.state.descLink} target={'_blank'}>
Link to markdown
</a>
) : (
proposal.info.state.descLink
),
}); });
}); });
return newListData; return newListData;
@ -46,13 +43,18 @@ export const ProposalsView = () => {
return ( return (
<Row> <Row style={{ background: `url(${tokenBackground})`, height: '100%' }}>
<Col flex="auto"> <Col flex="auto" xxl={15} xs={24} className="proposals-container">
<div className="proposals-header"> <div className="proposals-header">
<TokenIcon mintAddress={config?.info.governanceMint} size={40} /> <TokenIcon mintAddress={config?.info.governanceMint} size={60} style={{ marginRight: 20 }} />
<h1>{config?.info.name}</h1> <div>
<h1>{config?.info.name}</h1>
<a href={tokenMap.get(mint)?.extensions?.website} target="_blank">
{tokenMap.get(mint)?.extensions?.website}
</a>
</div>
<NewProposalMenuItem className="proposals-new-btn" /> <NewProposalMenuItem className="proposals-new-btn" disabled={!connected} />
</div> </div>
<List <List
itemLayout="vertical" itemLayout="vertical"

View File

@ -1,10 +1,16 @@
@import '~antd/dist/antd.dark.less'; @import '~antd/dist/antd.dark.less';
.proposals-container {
margin-left: auto;
margin-right: auto;
position: relative;
}
.proposals-header { .proposals-header {
margin-left: 20%;
margin-right: 20%;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0px 10px;
text-align: left;
h1 { h1 {
font-size: 28px; font-size: 28px;
@ -22,8 +28,6 @@
background-color: #202020; background-color: #202020;
padding: 0px !important; padding: 0px !important;
border-radius: 15px; border-radius: 15px;
margin-left: 20%;
margin-right: 20%;
:hover { :hover {
cursor: pointer; cursor: pointer;