mirror of https://github.com/certusone/oyster.git
WIP commit on proposals: have it pulling all timelock accounts and storing them clientside. Now need to fix formatting and stand up an index page.
This commit is contained in:
parent
143a50ce69
commit
9b091622bd
|
@ -0,0 +1,74 @@
|
|||
// credit https://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array
|
||||
export function toUTF8Array(str: string) {
|
||||
let utf8 = [];
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
let charcode = str.charCodeAt(i);
|
||||
if (charcode < 0x80) utf8.push(charcode);
|
||||
else if (charcode < 0x800) {
|
||||
utf8.push(0xc0 | (charcode >> 6), 0x80 | (charcode & 0x3f));
|
||||
} else if (charcode < 0xd800 || charcode >= 0xe000) {
|
||||
utf8.push(
|
||||
0xe0 | (charcode >> 12),
|
||||
0x80 | ((charcode >> 6) & 0x3f),
|
||||
0x80 | (charcode & 0x3f),
|
||||
);
|
||||
}
|
||||
// surrogate pair
|
||||
else {
|
||||
i++;
|
||||
// UTF-16 encodes 0x10000-0x10FFFF by
|
||||
// subtracting 0x10000 and splitting the
|
||||
// 20 bits of 0x0-0xFFFFF into two halves
|
||||
charcode =
|
||||
0x10000 + (((charcode & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
|
||||
utf8.push(
|
||||
0xf0 | (charcode >> 18),
|
||||
0x80 | ((charcode >> 12) & 0x3f),
|
||||
0x80 | ((charcode >> 6) & 0x3f),
|
||||
0x80 | (charcode & 0x3f),
|
||||
);
|
||||
}
|
||||
}
|
||||
return utf8;
|
||||
}
|
||||
|
||||
//courtesy https://gist.github.com/joni/3760795
|
||||
export function fromUTF8Array(data: number[]) {
|
||||
// array of bytes
|
||||
let str = '',
|
||||
i;
|
||||
|
||||
for (i = 0; i < data.length; i++) {
|
||||
const value = data[i];
|
||||
|
||||
if (value < 0x80) {
|
||||
str += String.fromCharCode(value);
|
||||
} else if (value > 0xbf && value < 0xe0) {
|
||||
str += String.fromCharCode(((value & 0x1f) << 6) | (data[i + 1] & 0x3f));
|
||||
i += 1;
|
||||
} else if (value > 0xdf && value < 0xf0) {
|
||||
str += String.fromCharCode(
|
||||
((value & 0x0f) << 12) |
|
||||
((data[i + 1] & 0x3f) << 6) |
|
||||
(data[i + 2] & 0x3f),
|
||||
);
|
||||
i += 2;
|
||||
} else {
|
||||
// surrogate pair
|
||||
const charCode =
|
||||
(((value & 0x07) << 18) |
|
||||
((data[i + 1] & 0x3f) << 12) |
|
||||
((data[i + 2] & 0x3f) << 6) |
|
||||
(data[i + 3] & 0x3f)) -
|
||||
0x010000;
|
||||
|
||||
str += String.fromCharCode(
|
||||
(charCode >> 10) | 0xd800,
|
||||
(charCode & 0x03ff) | 0xdc00,
|
||||
);
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
|
@ -18962,12 +18962,12 @@
|
|||
}
|
||||
},
|
||||
"@umijs/route-utils": {
|
||||
"version": "1.0.34",
|
||||
"resolved": "https://registry.npmjs.org/@umijs/route-utils/-/route-utils-1.0.34.tgz",
|
||||
"integrity": "sha512-5TUZCm5uuS3NbzdaoRDAfAIy6LDu+OCNL8xd+Dhth3YVFvbYkIeIG9/xl/5x1F+sWU8X/Oujlo6rfR8kgdB0Wg==",
|
||||
"version": "1.0.36",
|
||||
"resolved": "https://registry.npmjs.org/@umijs/route-utils/-/route-utils-1.0.36.tgz",
|
||||
"integrity": "sha512-e3TwjlqSmhZAQ3rIE++VJ3wliwZ4cGHZhvGtPS1QohCPkVILxA4oTJIyKgzJGUFEJcWbEB1CxiimLnPtZnBd/A==",
|
||||
"requires": {
|
||||
"@qixian.cs/path-to-regexp": "^6.1.0",
|
||||
"hash.js": "^1.1.7",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"memoize-one": "^5.1.1"
|
||||
}
|
||||
|
@ -21475,11 +21475,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"css-mediaquery": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz",
|
||||
"integrity": "sha1-aiw3NEkoYYYxxUvTPO3TAdoYvqA="
|
||||
},
|
||||
"css-prefers-color-scheme": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz",
|
||||
|
@ -24475,11 +24470,6 @@
|
|||
"strip-url-auth": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"hyphenate-style-name": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
|
||||
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
|
@ -26319,14 +26309,6 @@
|
|||
"object-visit": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"matchmediaquery": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.3.1.tgz",
|
||||
"integrity": "sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ==",
|
||||
"requires": {
|
||||
"css-mediaquery": "^0.1.2"
|
||||
}
|
||||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
|
@ -29612,17 +29594,6 @@
|
|||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"react-responsive": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.2.0.tgz",
|
||||
"integrity": "sha512-iagCqVrw4QSjhxKp3I/YK6+ODkWY6G+YPElvdYKiUUbywwh9Ds0M7r26Fj2/7dWFFbOpcGnJE6uE7aMck8j5Qg==",
|
||||
"requires": {
|
||||
"hyphenate-style-name": "^1.0.0",
|
||||
"matchmediaquery": "^0.3.0",
|
||||
"prop-types": "^15.6.1",
|
||||
"shallow-equal": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"react-router": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
|
||||
|
@ -32135,12 +32106,9 @@
|
|||
"integrity": "sha512-P/AgEKXphcN0L/8G5wLfbEz88mZQ9ayHS1OVESZaS2nxkN/msDWTGE8E1e9HXOWCZ9yoAfDrbVKAmTYpsYupFA=="
|
||||
},
|
||||
"use-media-antd-query": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/use-media-antd-query/-/use-media-antd-query-1.0.3.tgz",
|
||||
"integrity": "sha512-r8UnYLYYKwQIEIkWQQYs/ohJ0pqNGel9nbWHj5VBCtXtixT0ozj+X9PBlskv2HEOPfBLfnJrazpLgIyziXCU8A==",
|
||||
"requires": {
|
||||
"react-responsive": "^8.0.1"
|
||||
}
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/use-media-antd-query/-/use-media-antd-query-1.0.6.tgz",
|
||||
"integrity": "sha512-uEP116w7LH2z4MatYM0UMKD0lI4awPtXh2WrbeUfG5mCS1UD9D7lZVz9QsRThTIButxNnx9LjPSBEBD8G3Z5BA=="
|
||||
},
|
||||
"use-url-search-params": {
|
||||
"version": "2.3.13",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@import '~antd/es/style/themes/dark.less';
|
||||
@import "~antd/dist/antd.dark.less";
|
||||
@import '~antd/dist/antd.dark.less';
|
||||
|
||||
@primary-color: #ff00a8;
|
||||
@popover-background: #1a2029;
|
||||
|
|
|
@ -31,7 +31,7 @@ export const AppLayout = React.memo((props: any) => {
|
|||
[...Object.keys(paths)].find(key => location.pathname.startsWith(key)) ||
|
||||
'';
|
||||
const defaultKey = paths[current] || '1';
|
||||
const theme = 'light';
|
||||
const theme = 'dark';
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { contexts } from '@oyster/common';
|
||||
import { Card } from 'antd';
|
||||
import { useProposals } from '../../contexts/proposals';
|
||||
|
||||
export function Proposal() {
|
||||
const context = useProposals();
|
||||
const proposal =
|
||||
context.proposals['svXV4BxjpqQHVSNPoM15XecVj2RJ431xHrpxXDFfmGX'];
|
||||
console.log('please', proposal);
|
||||
return <Card>{proposal?.info.state.name}</Card>;
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
import React, { useContext, useEffect, useState } from 'react';
|
||||
|
||||
import {
|
||||
Connection,
|
||||
KeyedAccountInfo,
|
||||
PublicKeyAndAccount,
|
||||
} from '@solana/web3.js';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { contexts, utils, ParsedAccount } from '@oyster/common';
|
||||
import {
|
||||
TimelockSet,
|
||||
TimelockSetLayout,
|
||||
TimelockSetParser,
|
||||
} from '../models/timelock';
|
||||
|
||||
const { useConnectionConfig } = contexts.Connection;
|
||||
const { cache } = contexts.Accounts;
|
||||
|
||||
export interface ProposalsContextState {
|
||||
proposals: Record<string, ParsedAccount<TimelockSet>>;
|
||||
}
|
||||
|
||||
export const ProposalsContext = React.createContext<ProposalsContextState | null>(
|
||||
null,
|
||||
);
|
||||
export default function ProposalsProvider({ children = null as any }) {
|
||||
const { endpoint } = useConnectionConfig();
|
||||
|
||||
const connection = useMemo(() => new Connection(endpoint, 'recent'), [
|
||||
endpoint,
|
||||
]);
|
||||
const PROGRAM_IDS = utils.programIds();
|
||||
|
||||
const [proposals, setProposals] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
const queryProposals = async () => {
|
||||
const programAccounts = await connection.getProgramAccounts(
|
||||
PROGRAM_IDS.timelock.programId,
|
||||
);
|
||||
return programAccounts;
|
||||
};
|
||||
Promise.all([queryProposals()]).then(
|
||||
(all: PublicKeyAndAccount<Buffer>[][]) => {
|
||||
const newProposals: Record<string, ParsedAccount<TimelockSet>> = {};
|
||||
all[0].forEach(a => {
|
||||
if (a.account.data.length === TimelockSetLayout.span) {
|
||||
cache.add(a.pubkey, a.account, TimelockSetParser);
|
||||
const cached = cache.get(a.pubkey) as ParsedAccount<TimelockSet>;
|
||||
console.log('Got', a.pubkey.toBase58());
|
||||
newProposals[a.pubkey.toBase58()] = cached;
|
||||
}
|
||||
});
|
||||
setProposals(newProposals);
|
||||
},
|
||||
);
|
||||
const subID = connection.onProgramAccountChange(
|
||||
PROGRAM_IDS.timelock.programId,
|
||||
async (info: KeyedAccountInfo) => {
|
||||
if (info.accountInfo.data.length === TimelockSetLayout.span) {
|
||||
cache.add(info.accountId, info.accountInfo, TimelockSetParser);
|
||||
const cached = cache.get(
|
||||
info.accountId,
|
||||
) as ParsedAccount<TimelockSet>;
|
||||
setProposals(proposals => ({
|
||||
...proposals,
|
||||
[info.accountId.toBase58()]: cached,
|
||||
}));
|
||||
}
|
||||
},
|
||||
'singleGossip',
|
||||
);
|
||||
return () => {
|
||||
connection.removeProgramAccountChangeListener(subID);
|
||||
};
|
||||
}, [connection, PROGRAM_IDS.timelock.programAccountId]);
|
||||
|
||||
return (
|
||||
<ProposalsContext.Provider value={{ proposals }}>
|
||||
{children}
|
||||
</ProposalsContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useProposals = () => {
|
||||
const context = useContext(ProposalsContext);
|
||||
return context as ProposalsContextState;
|
||||
};
|
|
@ -2,7 +2,7 @@ import { HashRouter, Route, Switch } from 'react-router-dom';
|
|||
import React from 'react';
|
||||
import { contexts } from '@oyster/common';
|
||||
import { AppLayout } from './components/Layout';
|
||||
|
||||
import ProposalsProvider from './contexts/proposals';
|
||||
import { DashboardView, HomeView } from './views';
|
||||
const { WalletProvider } = contexts.Wallet;
|
||||
const { ConnectionProvider } = contexts.Connection;
|
||||
|
@ -15,12 +15,18 @@ export function Routes() {
|
|||
<ConnectionProvider>
|
||||
<WalletProvider>
|
||||
<AccountsProvider>
|
||||
<AppLayout>
|
||||
<Switch>
|
||||
<Route exact path="/" component={() => <HomeView />} />
|
||||
<Route exact path="/dashboard" children={<DashboardView />} />
|
||||
</Switch>
|
||||
</AppLayout>
|
||||
<ProposalsProvider>
|
||||
<AppLayout>
|
||||
<Switch>
|
||||
<Route exact path="/" component={() => <HomeView />} />
|
||||
<Route
|
||||
exact
|
||||
path="/dashboard"
|
||||
children={<DashboardView />}
|
||||
/>
|
||||
</Switch>
|
||||
</AppLayout>
|
||||
</ProposalsProvider>
|
||||
</AccountsProvider>
|
||||
</WalletProvider>
|
||||
</ConnectionProvider>
|
||||
|
|
|
@ -4,6 +4,8 @@ import { GUTTER } from '../../constants';
|
|||
import { Button } from 'antd';
|
||||
import { createProposal } from '../../actions/createProposal';
|
||||
import { contexts } from '@oyster/common';
|
||||
import { Proposal } from '../../components/Proposal';
|
||||
import { ProposalsContext } from '../../contexts/proposals';
|
||||
const { useWallet } = contexts.Wallet;
|
||||
const { useConnection } = contexts.Connection;
|
||||
export const HomeView = () => {
|
||||
|
@ -15,6 +17,7 @@ export const HomeView = () => {
|
|||
<Button onClick={() => createProposal(connection, wallet.wallet)}>
|
||||
Click me
|
||||
</Button>
|
||||
<Proposal />
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue