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

View File

@ -23,7 +23,7 @@ export function Routes() {
<Switch>
<Route exact path="/" component={() => <HomeView />} />
<Route path="/proposal/:id" children={<ProposalView />} />
<Route path="/proposals/:id" children={<ProposalsView />} />
<Route path="/governance/:id" children={<ProposalsView />} />
<Route
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 { 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 { TokenIcon } from '@oyster/common';
import { TokenIcon, useConnectionConfig, useWallet } from '@oyster/common';
import { Background } from './../../components/Background';
import { useHistory } from 'react-router-dom';
import { RegisterGovernanceMenuItem } from '../governance/register';
import { TimelockStateStatus } from '../../models/timelock';
export const HomeView = () => {
const history = useHistory();
const { configs } = useProposals();
const [page, setPage] = useState(0);
const { configs, proposals } = useProposals();
const { connected } = useWallet();
const listData = useMemo(() => {
const newListData: any[] = [];
Object.keys(configs).forEach(key => {
const config = configs[key];
Object.keys(configs).forEach(configKey => {
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({
href: '/proposals/' + key,
href: '/governance/' + configKey,
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,
});
});
return newListData;
}, [configs]);
}, [configs, proposals]);
return (
<Row>
<Col flex="auto">
<Background />
<div className="governance-title">
<h1>Governance</h1>
<RegisterGovernanceMenuItem style={{ marginLeft: 'auto', marginRight: 0 }} />
</div>
<List
itemLayout="vertical"
size="large"
loading={listData.length === 0}
pagination={false}
dataSource={listData}
renderItem={item => (
<List.Item key={item.title} className="governance-item" onClick={() => history.push(item.href)}>
<List.Item.Meta
avatar={item.badge}
title={item.title}
description={item.description}
/>
</List.Item>
)}
/>
</Col>
</Row>
<>
<Background />
<Row>
<Col flex="auto" xxl={15} xs={24} className="governance-container">
<div className="governance-title">
<h1>Governance</h1>
<RegisterGovernanceMenuItem style={{ marginLeft: 'auto', marginRight: 0 }} disabled={!connected} />
</div>
<List
itemLayout="vertical"
size="large"
loading={listData.length === 0}
pagination={false}
dataSource={listData}
renderItem={item => (
<List.Item key={item.title} className="governance-item" onClick={() => history.push(item.href)}
extra={
<>
<Statistic title="Proposals" value={item.proposalCount.total} />
</>
}>
<List.Item.Meta
avatar={item.badge}
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';
.governance-container {
margin-left: auto;
margin-right: auto;
position: relative;
}
.governance-title {
font-family: "FF Oxide Solid";
font-style: normal;
font-weight: 300;
letter-spacing: 0px;
position: absolute;
top: 240px;
left: calc(20% + 10px);
top: -40px;
z-index: 2;
display: flex;
align-items: center;
right: calc(20% + 10px);
width: 100%;
padding: 0px 10px;
h1 {
font-size: 28px;
@ -24,13 +30,32 @@
background-color: #202020;
padding: 0px !important;
border-radius: 15px;
margin-left: 20%;
margin-right: 20%;
:hover {
cursor: pointer;
background-color: #252425;
border-radius: 15px;
.ant-badge-count {
top: 8px !important;
right: 12px !important;
}
.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 {
@ -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 { 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 { StateBadge, StateBadgeRibbon } from '../../components/Proposal/StateBadge';
import { urlRegex } from '../proposal';
import { StateBadge } from '../../components/Proposal/StateBadge';
import { useHistory, useParams } from 'react-router-dom';
import { TokenIcon } from '@oyster/common';
import { TokenIcon, useConnectionConfig, useWallet } from '@oyster/common';
import { NewProposalMenuItem } from '../proposal/new';
const PAGE_SIZE = 10;
@ -15,6 +14,13 @@ export const ProposalsView = () => {
const { proposals } = useProposals();
const config = useConfig(id);
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 newListData: any[] = [];
@ -24,21 +30,12 @@ export const ProposalsView = () => {
return;
}
newListData.push({
href: '/proposal/' + key,
title: proposal.info.state.name,
badge: <TokenIcon mintAddress={config?.info.governanceMint} size={30} />,
badge: <TokenIcon mintAddress={mint} size={30} />,
status: proposal.info.state.status,
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;
@ -46,13 +43,18 @@ export const ProposalsView = () => {
return (
<Row>
<Col flex="auto">
<Row style={{ background: `url(${tokenBackground})`, height: '100%' }}>
<Col flex="auto" xxl={15} xs={24} className="proposals-container">
<div className="proposals-header">
<TokenIcon mintAddress={config?.info.governanceMint} size={40} />
<h1>{config?.info.name}</h1>
<TokenIcon mintAddress={config?.info.governanceMint} size={60} style={{ marginRight: 20 }} />
<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>
<List
itemLayout="vertical"

View File

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