diff --git a/package.json b/package.json index 105037ba6d..ac8c77ea47 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,12 @@ "pioneer", "pioneer/packages/apps*", "pioneer/packages/page*", - "pioneer/packages/react*" + "pioneer/packages/react*", + "pioneer/packages/joy-utils", + "pioneer/packages/joy-members", + "pioneer/packages/joy-pages", + "pioneer/packages/joy-election", + "pioneer/packages/joy-proposals" ], "resolutions": { "@polkadot/api": "1.26.1", diff --git a/pioneer/.eslintignore b/pioneer/.eslintignore index e24e58eaba..6abdb1d141 100644 --- a/pioneer/.eslintignore +++ b/pioneer/.eslintignore @@ -2,16 +2,11 @@ **/coverage/* **/node_modules/* packages/old-apps/* -packages/joy-members/* -packages/joy-election/* packages/joy-forum/* packages/joy-help/* packages/joy-media/* -packages/joy-pages/* -packages/joy-proposals/* packages/joy-roles/* packages/joy-settings/* -packages/joy-utils/* packages/joy-utils-old/* .eslintrc.js i18next-scanner.config.js diff --git a/pioneer/.eslintrc.js b/pioneer/.eslintrc.js index ada8259c1e..a909f51bfc 100644 --- a/pioneer/.eslintrc.js +++ b/pioneer/.eslintrc.js @@ -13,14 +13,20 @@ module.exports = { rules: { ...base.rules, '@typescript-eslint/no-explicit-any': 'off', - 'react/prop-types': 'off', 'new-cap': 'off', '@typescript-eslint/interface-name-prefix': 'off', '@typescript-eslint/ban-ts-comment': 'error', // why only required in VSCode!?!? is eslint plugin not working like eslint commandline? // Or are we having to add this because of new versions of eslint-config-* ? 'no-console': 'off', - 'header/header': 'off' // Temporary disable polkadot's rule + // Override some extended config rules: + 'camelcase': 'off', + 'header/header': 'off', + 'sort-keys': 'off', + 'react/jsx-sort-props': 'off', + 'react/jsx-max-props-per-line': 'off', + 'sort-destructure-keys/sort-destructure-keys': 'off', + '@typescript-eslint/unbound-method': 'warn', // Doesn't work well with our version of Formik, see: https://github.com/formium/formik/issues/2589 }, // isolate pioneer from monorepo eslint rules root: true diff --git a/pioneer/packages/apps-config/src/api/spec/index.ts b/pioneer/packages/apps-config/src/api/spec/index.ts index d65760b541..22e2122aeb 100644 --- a/pioneer/packages/apps-config/src/api/spec/index.ts +++ b/pioneer/packages/apps-config/src/api/spec/index.ts @@ -10,6 +10,7 @@ import encointerNodeTeeproxy from './encointer-node-teeproxy'; import kulupu from './kulupu'; import nodeTemplate from './node-template'; import stablePoc from './stable-poc'; +import joystreamNode from './joystream-node'; export default { acala, @@ -21,5 +22,6 @@ export default { kulupu, 'node-template': nodeTemplate, 'stable-poc': stablePoc, - stable_poc: stablePoc + stable_poc: stablePoc, + 'joystream-node': joystreamNode }; diff --git a/pioneer/packages/apps-config/src/api/spec/joystream-node.ts b/pioneer/packages/apps-config/src/api/spec/joystream-node.ts new file mode 100644 index 0000000000..7e436a96d4 --- /dev/null +++ b/pioneer/packages/apps-config/src/api/spec/joystream-node.ts @@ -0,0 +1,3 @@ +import { types } from '@joystream/types'; + +export default types; diff --git a/pioneer/packages/apps-config/src/settings/endpoints.ts b/pioneer/packages/apps-config/src/settings/endpoints.ts index 0b1b5dd42c..c7d662f519 100644 --- a/pioneer/packages/apps-config/src/settings/endpoints.ts +++ b/pioneer/packages/apps-config/src/settings/endpoints.ts @@ -29,6 +29,11 @@ function createDev (t: TFunction): LinkOption[] { function createLive (t: TFunction): LinkOption[] { return [ + { + info: 'joystream', + text: t('rpc.joystream', 'Joystream (Current Testnet, hosted by Jsgenesis)', { ns: 'apps-config' }), + value: 'wss://rome-rpc-endpoint.joystream.org:9944' + }, { dnslink: 'polkadot', info: 'polkadot', diff --git a/pioneer/packages/apps-config/src/ui/logos/index.ts b/pioneer/packages/apps-config/src/ui/logos/index.ts index 93f37ff13f..b4d0036715 100644 --- a/pioneer/packages/apps-config/src/ui/logos/index.ts +++ b/pioneer/packages/apps-config/src/ui/logos/index.ts @@ -17,6 +17,7 @@ import nodeNodle from './nodes/nodle.svg'; import nodePolkadot from './nodes/polkadot-circle.svg'; import nodePolkadotJs from './nodes/polkadot-js.svg'; import nodeSubstrate from './nodes/substrate-hexagon.svg'; +import nodeJoystream from './nodes/joystream-node.svg'; // extensions import extensionPolkadotJs from './extensions/polkadot-js.svg'; @@ -48,7 +49,8 @@ const nodeLogos: Record = [ ['Nodle Chain Node', nodeNodle], ['parity-polkadot', nodePolkadot], ['polkadot-js', nodePolkadotJs], - ['substrate-node', nodeSubstrate] + ['substrate-node', nodeSubstrate], + ['joystream-node', nodeJoystream] ].reduce((logos, [node, logo]): Record => ({ ...logos, [(node as string).toLowerCase().replace(/-/g, ' ')]: logo diff --git a/pioneer/packages/apps-config/src/ui/logos/nodes/joystream-node.svg b/pioneer/packages/apps-config/src/ui/logos/nodes/joystream-node.svg new file mode 100755 index 0000000000..67dd1b71f2 --- /dev/null +++ b/pioneer/packages/apps-config/src/ui/logos/nodes/joystream-node.svg @@ -0,0 +1 @@ +Icon-mono-white-1bg-blue \ No newline at end of file diff --git a/pioneer/packages/apps-routing/src/index.ts b/pioneer/packages/apps-routing/src/index.ts index cc5eab5943..597d45b1a3 100644 --- a/pioneer/packages/apps-routing/src/index.ts +++ b/pioneer/packages/apps-routing/src/index.ts @@ -9,69 +9,49 @@ import appSettings from '@polkadot/ui-settings'; // When adding here, also ensure to add to Dummy.tsx import accounts from './accounts'; -import claims from './claims'; -import contracts from './contracts'; -import council from './council'; -// import dashboard from './dashboard'; -import democracy from './democracy'; import explorer from './explorer'; import extrinsics from './extrinsics'; -import genericAsset from './generic-asset'; import js from './js'; -import parachains from './parachains'; -import poll from './poll'; import settings from './settings'; -import society from './society'; -import staking from './staking'; import storage from './storage'; import sudo from './sudo'; -import techcomm from './techcomm'; import toolbox from './toolbox'; import transfer from './transfer'; -import treasury from './treasury'; +// Joy packages +import members from './joy-members'; +import { terms, privacyPolicy } from './joy-pages'; +import election from './joy-election'; +import proposals from './joy-proposals'; export default function create (t: (key: string, text: string, options: { ns: string }) => T): Routes { return appSettings.uiMode === 'light' ? [ - // dashboard, - explorer(t), - accounts(t), - claims(t), - poll(t), - transfer(t), - genericAsset(t), - null, - staking(t), - democracy(t), - council(t), - // TODO Not sure about the inclusion of treasury, parachains & society here + members(t), + election(t), + proposals(t), null, + transfer(t), + accounts(t), settings(t) ] : [ - // dashboard(t), - explorer(t), - accounts(t), - claims(t), - poll(t), - transfer(t), - genericAsset(t), + members(t), + election(t), + proposals(t), null, - staking(t), - democracy(t), - council(t), - treasury(t), - techcomm(t), - parachains(t), - society(t), + transfer(t), + accounts(t), + settings(t), null, - contracts(t), + explorer(t), storage(t), extrinsics(t), + js(t), + toolbox(t), sudo(t), null, - settings(t), - toolbox(t), - js(t) + // Those are hidden + terms(t), + privacyPolicy(t) ]; } diff --git a/pioneer/packages/apps-routing/src/joy-election.ts b/pioneer/packages/apps-routing/src/joy-election.ts new file mode 100644 index 0000000000..cbff554d99 --- /dev/null +++ b/pioneer/packages/apps-routing/src/joy-election.ts @@ -0,0 +1,17 @@ +import { Route } from './types'; + +import Election from '@polkadot/joy-election/index'; +import SidebarSubtitle from '@polkadot/joy-election/SidebarSubtitle'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component: Election, + display: { + needsApi: ['query.council.activeCouncil', 'query.councilElection.stage'] + }, + text: t('nav.election', 'Council', { ns: 'apps-routing' }), + icon: 'university', + name: 'council', + SubtitleComponent: SidebarSubtitle + }; +} diff --git a/pioneer/packages/apps-routing/src/joy-members.ts b/pioneer/packages/apps-routing/src/joy-members.ts new file mode 100644 index 0000000000..e8c3712d4d --- /dev/null +++ b/pioneer/packages/apps-routing/src/joy-members.ts @@ -0,0 +1,15 @@ +import { Route } from './types'; + +import Members from '@polkadot/joy-members/index'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component: Members, + display: { + needsApi: ['query.members.nextMemberId'] + }, + icon: 'users', + name: 'members', + text: t('nav.membership', 'Membership', { ns: 'apps-routing' }) + }; +} diff --git a/pioneer/packages/apps-routing/src/joy-pages.ts b/pioneer/packages/apps-routing/src/joy-pages.ts new file mode 100644 index 0000000000..a39470e322 --- /dev/null +++ b/pioneer/packages/apps-routing/src/joy-pages.ts @@ -0,0 +1,27 @@ +import { Route } from './types'; + +import { ToS, Privacy } from '@polkadot/joy-pages/index'; + +export function terms (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component: ToS, + display: { + isHidden: true + }, + text: t('nav.terms', 'Terms of Service', { ns: 'apps-routing' }), + icon: 'file', + name: 'pages/tos' + }; +} + +export function privacyPolicy (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component: Privacy, + display: { + isHidden: true + }, + text: t('nav.privacy', 'Privacy Policy', { ns: 'apps-routing' }), + icon: 'file', + name: 'pages/privacy' + }; +} diff --git a/pioneer/packages/apps-routing/src/joy-proposals.ts b/pioneer/packages/apps-routing/src/joy-proposals.ts new file mode 100644 index 0000000000..4c76e21eb8 --- /dev/null +++ b/pioneer/packages/apps-routing/src/joy-proposals.ts @@ -0,0 +1,16 @@ +import { Route } from './types'; + +import Proposals from '@polkadot/joy-proposals/index'; + +export default function create (t: (key: string, text: string, options: { ns: string }) => T): Route { + return { + Component: Proposals, + display: { + needsApi: ['query.proposalsEngine.proposalCount'] + }, + text: t('nav.proposals', 'Proposals', { ns: 'apps-routing' }), + icon: 'tasks', + name: 'proposals' + // TODO: useCounter with active proposals count? (could be a nice addition) + }; +} diff --git a/pioneer/packages/apps-routing/src/types.ts b/pioneer/packages/apps-routing/src/types.ts index 6dce432d8c..4a6695fef2 100644 --- a/pioneer/packages/apps-routing/src/types.ts +++ b/pioneer/packages/apps-routing/src/types.ts @@ -24,6 +24,8 @@ export interface Route { name: string; text: string; useCounter?: () => number | string | null; + // Joystream-specific + SubtitleComponent?: React.ComponentType; } export type Routes = (Route | null)[]; diff --git a/pioneer/packages/apps/public/favicon.ico b/pioneer/packages/apps/public/favicon.ico index ec3c167a5b..a3d783d522 100644 Binary files a/pioneer/packages/apps/public/favicon.ico and b/pioneer/packages/apps/public/favicon.ico differ diff --git a/pioneer/packages/apps/public/index.html b/pioneer/packages/apps/public/index.html index c9efd8fa30..d8318bd0c3 100644 --- a/pioneer/packages/apps/public/index.html +++ b/pioneer/packages/apps/public/index.html @@ -6,6 +6,17 @@ <%= htmlWebpackPlugin.options.PAGE_TITLE %> + <% if (htmlWebpackPlugin.options.IS_PROD) { %> + + + + <% } %> diff --git a/pioneer/packages/apps/src/Apps.tsx b/pioneer/packages/apps/src/Apps.tsx index f0995e28e5..c331f9f976 100644 --- a/pioneer/packages/apps/src/Apps.tsx +++ b/pioneer/packages/apps/src/Apps.tsx @@ -21,6 +21,9 @@ import SideBar from './SideBar'; import WarmUp from './WarmUp'; import { WindowDimensionsCtx } from './WindowDimensions'; +/* Joystream-specific */ +import TopBar from './JoyTopBar/TopBar'; + interface SidebarState { isCollapsed: boolean; isMenu: boolean; @@ -96,7 +99,10 @@ function Apps ({ className = '' }: Props): React.ReactElement { toggleMenu={_toggleMenu} /> - +
+ + +
@@ -222,4 +228,11 @@ export default React.memo(styled(Apps)` opacity: 1; } } + + .apps--Main { + flex-grow: 1; + min-height: 100vh; + overflow-x: hidden; + overflow-y: auto; + } `); diff --git a/pioneer/packages/apps/src/Content/index.tsx b/pioneer/packages/apps/src/Content/index.tsx index 66ea117e5d..cf2dc00224 100644 --- a/pioneer/packages/apps/src/Content/index.tsx +++ b/pioneer/packages/apps/src/Content/index.tsx @@ -16,6 +16,11 @@ import { useTranslation } from '../translate'; import NotFound from './NotFound'; import Status from './Status'; +// Joystream-specific +// We must use transport provider here instead of /apps/src/index +// to avoid "Cannot create Transport: The Substrate API is not ready yet." error +import { TransportProvider } from '@polkadot/joy-utils/react/context'; + interface Props { className?: string; } @@ -60,11 +65,25 @@ function Content ({ className }: Props): React.ReactElement { ? : ( - + { needsApi + // Add transport provider for routes that need the api + // (the above condition makes sure it's aleady initialized at this point) + ? ( + + + + ) + : ( + + ) } ) } @@ -78,15 +97,11 @@ function Content ({ className }: Props): React.ReactElement { } export default React.memo(styled(Content)` - background: #f5f4f3; - flex-grow: 1; - height: 100%; - min-height: 100vh; - overflow-x: hidden; - overflow-y: auto; + background: rgba(250, 250, 250); padding: 0 1.5rem; position: relative; width: 100%; + height: 100%; @media(max-width: 768px) { padding: 0 0.5rem; diff --git a/pioneer/packages/apps/src/JoyTopBar/TopBar.tsx b/pioneer/packages/apps/src/JoyTopBar/TopBar.tsx new file mode 100644 index 0000000000..807141b73c --- /dev/null +++ b/pioneer/packages/apps/src/JoyTopBar/TopBar.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { useMyMembership } from '@polkadot/joy-utils/react/hooks'; +import { InputAddress } from '@polkadot/react-components'; +import { Available } from '@polkadot/react-query'; +import styled from 'styled-components'; +import { useApi } from '@polkadot/react-hooks'; + +const StyledTopBar = styled.div` + padding: 0.75rem; + background-color: #3f3f3f; + border-bottom: 1px solid #d4d4d5; + text-align: right; + margin: 0; + + &.NoMyAddress { + background-color: #ffeb83; + color: #000; + text-align: center; + } + + .ui--InputAddress { + display: inline-block; + } +`; + +function JoyTopBar () { + const { + allAccounts, + myAddress + } = useMyMembership(); + + const { isApiReady } = useApi(); + + if (!isApiReady) { + return null; + } + + const balance = Balance: ; + const labelExtra = myAddress + ? + : 'No key selected'; + + return Object.keys(allAccounts || {}).length ? ( + + + + ) : null; +} + +export default JoyTopBar; diff --git a/pioneer/packages/apps/src/SideBar/ChainInfo.tsx b/pioneer/packages/apps/src/SideBar/ChainInfo.tsx index 0e417ee81e..737bed301b 100644 --- a/pioneer/packages/apps/src/SideBar/ChainInfo.tsx +++ b/pioneer/packages/apps/src/SideBar/ChainInfo.tsx @@ -24,7 +24,7 @@ function ChainInfo ({ className = '', onClick }: Props): React.ReactElement
diff --git a/pioneer/packages/apps/src/SideBar/Item.tsx b/pioneer/packages/apps/src/SideBar/Item.tsx index abcd801144..629f9c9c19 100644 --- a/pioneer/packages/apps/src/SideBar/Item.tsx +++ b/pioneer/packages/apps/src/SideBar/Item.tsx @@ -79,12 +79,15 @@ function Item ({ isCollapsed, onClick, route }: Props): React.ReactElement - {text} + + {text} + { SubtitleComponent && } + {!!count && (
@@ -159,7 +138,7 @@ export default React.memo(styled(SideBar)` .apps--SideBar { align-items: center; - background: #4f5255; + background: #3f3f3f; box-sizing: border-box; display: flex; flex-flow: column; @@ -221,7 +200,7 @@ export default React.memo(styled(SideBar)` } .apps--SideBar-collapse { - background: #4f5255; + background: #3f3f3f; bottom: 0; left: 0; padding: 0.75rem 0 .75rem 0.65rem; diff --git a/pioneer/packages/apps/src/index.tsx b/pioneer/packages/apps/src/index.tsx index 1654bc4541..df91ca7b15 100644 --- a/pioneer/packages/apps/src/index.tsx +++ b/pioneer/packages/apps/src/index.tsx @@ -20,6 +20,9 @@ import settings from '@polkadot/ui-settings'; import Apps from './Apps'; import WindowDimensions from './WindowDimensions'; +/* Joystream-specific */ +import { MyMembershipProvider, MyAccountProvider } from '@polkadot/joy-utils/react/context'; + const rootId = 'root'; const rootElement = document.getElementById(rootId); const theme = { theme: settings.uiTheme }; @@ -38,19 +41,23 @@ store.each((_, key): void => { ReactDOM.render( - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + , rootElement diff --git a/pioneer/packages/apps/webpack.base.config.js b/pioneer/packages/apps/webpack.base.config.js index 4fb1d34154..c0f79a4010 100644 --- a/pioneer/packages/apps/webpack.base.config.js +++ b/pioneer/packages/apps/webpack.base.config.js @@ -98,7 +98,7 @@ function createWebpack (ENV, context) { ] }, { - exclude: [/semantic-ui-css/], + // Original config had "exclude: [/semantic-ui-css/]" test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], use: [ { @@ -112,7 +112,8 @@ function createWebpack (ENV, context) { ] }, { - exclude: [/semantic-ui-css/], + // Original config had "exclude: [/semantic-ui-css/]", because Semantic UI Icons + // are not used in polkadot-js/apps repository, but they are used in ours test: [/\.eot$/, /\.ttf$/, /\.svg$/, /\.woff$/, /\.woff2$/], use: [ { @@ -123,15 +124,6 @@ function createWebpack (ENV, context) { } } ] - }, - { - include: [/semantic-ui-css/], - test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.eot$/, /\.ttf$/, /\.svg$/, /\.woff$/, /\.woff2$/], - use: [ - { - loader: require.resolve('null-loader') - } - ] } ] }, diff --git a/pioneer/packages/apps/webpack.config.js b/pioneer/packages/apps/webpack.config.js index f31f22bbe1..041f8f8356 100644 --- a/pioneer/packages/apps/webpack.config.js +++ b/pioneer/packages/apps/webpack.config.js @@ -20,7 +20,8 @@ module.exports = merge( devtool: process.env.BUILD_ANALYZE ? 'source-map' : false, plugins: [ new HtmlWebpackPlugin({ - PAGE_TITLE: 'Polkadot/Substrate Portal', + IS_PROD: ENV === 'production', + PAGE_TITLE: 'Joystream Network Portal', inject: true, template: path.join(context, `${hasPublic ? 'public/' : ''}index.html`) }) diff --git a/pioneer/packages/joy-election/.skip-build b/pioneer/packages/joy-election/.skip-build deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/pioneer/packages/joy-election/package.json b/pioneer/packages/joy-election/package.json index 2bee08d3ff..17c74cd58c 100644 --- a/pioneer/packages/joy-election/package.json +++ b/pioneer/packages/joy-election/package.json @@ -7,9 +7,9 @@ "author": "Joystream contributors", "maintainers": [], "dependencies": { - "@babel/runtime": "^7.7.1", - "@polkadot/react-components": "0.37.0-beta.63", - "@polkadot/react-query": "0.37.0-beta.63", + "@babel/runtime": "^7.10.5", + "@polkadot/react-components": "0.51.1", + "@polkadot/react-query": "0.51.1", "@polkadot/joy-utils": "^0.1.1" } } diff --git a/pioneer/packages/joy-election/src/Applicant.tsx b/pioneer/packages/joy-election/src/Applicant.tsx index b48c3291f3..0626c53e1e 100644 --- a/pioneer/packages/joy-election/src/Applicant.tsx +++ b/pioneer/packages/joy-election/src/Applicant.tsx @@ -4,24 +4,25 @@ import { Table } from 'semantic-ui-react'; import { I18nProps } from '@polkadot/react-components/types'; import { ApiProps } from '@polkadot/react-api/types'; -import { withCalls } from '@polkadot/react-api/with'; +import { withCalls } from '@polkadot/react-api/hoc'; import { AccountId } from '@polkadot/types/interfaces'; import { formatBalance } from '@polkadot/util'; import CandidatePreview from './CandidatePreview'; import translate from './translate'; -import { calcTotalStake } from '@polkadot/joy-utils/index'; +import { calcTotalStake } from '@polkadot/joy-utils/functions/misc'; import { ElectionStake } from '@joystream/types/council'; type Props = ApiProps & I18nProps & { index: number; accountId: AccountId; stake?: ElectionStake; + isVotingStage: boolean; }; class Applicant extends React.PureComponent { render () { - const { index, accountId, stake } = this.props; + const { index, accountId, stake, isVotingStage } = this.props; const voteUrl = `/council/votes?applicantId=${accountId.toString()}`; return ( @@ -33,9 +34,11 @@ class Applicant extends React.PureComponent { {formatBalance(calcTotalStake(stake))} - - Vote - + { isVotingStage && ( + + Vote + + ) } ); } diff --git a/pioneer/packages/joy-election/src/Applicants.tsx b/pioneer/packages/joy-election/src/Applicants.tsx index e84c85f3d5..3ee84db4d7 100644 --- a/pioneer/packages/joy-election/src/Applicants.tsx +++ b/pioneer/packages/joy-election/src/Applicants.tsx @@ -1,49 +1,68 @@ import React from 'react'; -import { Table } from 'semantic-ui-react'; +import { Table, Message } from 'semantic-ui-react'; import BN from 'bn.js'; import { I18nProps } from '@polkadot/react-components/types'; import { ApiProps } from '@polkadot/react-api/types'; -import { withCalls } from '@polkadot/react-api/with'; +import { withCalls } from '@polkadot/react-api/hoc'; import { AccountId } from '@polkadot/types/interfaces'; +import { Option } from '@polkadot/types'; import { formatNumber } from '@polkadot/util'; import translate from './translate'; import Applicant from './Applicant'; import ApplyForm from './ApplyForm'; -import Section from '@polkadot/joy-utils/Section'; -import { queryToProp } from '@polkadot/joy-utils/index'; -import { withMyAccount, MyAccountProps } from '@polkadot/joy-utils/MyAccount'; +import Section from '@polkadot/joy-utils/react/components/Section'; +import { queryToProp } from '@polkadot/joy-utils/functions/misc'; +import { withMyAccount, MyAccountProps } from '@polkadot/joy-utils/react/hocs/accounts'; +import { ElectionStage } from '@joystream/types/src/council'; +import { RouteProps } from 'react-router-dom'; -type Props = ApiProps & I18nProps & MyAccountProps & { +type Props = RouteProps & ApiProps & I18nProps & MyAccountProps & { candidacyLimit?: BN; applicants?: Array; + stage?: Option; }; class Applicants extends React.PureComponent { - private renderTable = (applicants: Array) => ( - - - - # - Applicant - Total stake - Actions - - - {applicants.map((accountId, index) => ( - - ))} -
- ) + private renderTable = (applicants: Array) => { + const isVotingStage = this.props.stage?.unwrapOr(undefined)?.isOfType('Voting') || false; + + return ( + + + + # + Applicant + Total stake + { isVotingStage && ( + Actions + ) } + + + {applicants.map((accountId, index) => ( + + ))} +
+ ); + } render () { - const { myAddress, applicants = [], candidacyLimit = new BN(0) } = this.props; + const { myAddress, applicants = [], candidacyLimit = new BN(0), stage } = this.props; const title = Applicants {applicants.length}/{formatNumber(candidacyLimit)}; return <>
- + { stage?.unwrapOr(undefined)?.isOfType('Announcing') + ? ( + + ) + : ( + + Applying to council is only possible during Announcing stage. + + ) + }
{!applicants.length @@ -59,6 +78,7 @@ class Applicants extends React.PureComponent { export default translate( withCalls( queryToProp('query.councilElection.candidacyLimit'), - queryToProp('query.councilElection.applicants') + queryToProp('query.councilElection.applicants'), + queryToProp('query.councilElection.stage') )(withMyAccount(Applicants)) ); diff --git a/pioneer/packages/joy-election/src/ApplyForm.tsx b/pioneer/packages/joy-election/src/ApplyForm.tsx index f058fc992c..518d95ce51 100644 --- a/pioneer/packages/joy-election/src/ApplyForm.tsx +++ b/pioneer/packages/joy-election/src/ApplyForm.tsx @@ -3,21 +3,21 @@ import React from 'react'; import { I18nProps } from '@polkadot/react-components/types'; import { ApiProps } from '@polkadot/react-api/types'; -import { withCalls, withMulti } from '@polkadot/react-api/with'; +import { withCalls, withMulti } from '@polkadot/react-api/hoc'; import { Labelled } from '@polkadot/react-components/index'; import { Balance } from '@polkadot/types/interfaces'; import translate from './translate'; -import TxButton from '@polkadot/joy-utils/TxButton'; -import InputStake from '@polkadot/joy-utils/InputStake'; +import TxButton from '@polkadot/joy-utils/react/components/TxButton'; +import InputStake from '@polkadot/joy-utils/react/components/InputStake'; import { ElectionStake } from '@joystream/types/council'; -import { calcTotalStake, ZERO } from '@polkadot/joy-utils/index'; -import { MyAddressProps, withOnlyMembers } from '@polkadot/joy-utils/MyAccount'; +import { calcTotalStake, ZERO } from '@polkadot/joy-utils/functions/misc'; +import { MyAddressProps } from '@polkadot/joy-utils/react/hocs/accounts'; +import { withOnlyMembers } from '@polkadot/joy-utils/react/hocs/guards'; type Props = ApiProps & I18nProps & MyAddressProps & { minStake?: Balance; alreadyStaked?: ElectionStake; - myBalance?: Balance; }; type State = { @@ -48,15 +48,16 @@ class ApplyForm extends React.PureComponent { isValid={isStakeValid} onChange={this.onChangeStake} /> - - - +
+ + + +
); } @@ -71,10 +72,9 @@ class ApplyForm extends React.PureComponent { private onChangeStake = (stake?: BN): void => { stake = stake || ZERO; - const { myBalance = ZERO } = this.props; - const isStakeLteBalance = stake.lte(myBalance); const isStakeGteMinStake = stake.add(this.alreadyStaked()).gte(this.minStake()); - const isStakeValid = !stake.isZero() && isStakeGteMinStake && isStakeLteBalance; + const isStakeValid = !stake.isZero() && isStakeGteMinStake; + this.setState({ stake, isStakeValid }); } } @@ -88,8 +88,6 @@ export default withMulti( ['query.councilElection.minCouncilStake', { propName: 'minStake' }], ['query.councilElection.applicantStakes', - { paramName: 'myAddress', propName: 'alreadyStaked' }], - ['query.balances.freeBalance', - { paramName: 'myAddress', propName: 'myBalance' }] + { paramName: 'myAddress', propName: 'alreadyStaked' }] ) ); diff --git a/pioneer/packages/joy-election/src/CandidatePreview.tsx b/pioneer/packages/joy-election/src/CandidatePreview.tsx index 9e6571efb6..bab07182e7 100644 --- a/pioneer/packages/joy-election/src/CandidatePreview.tsx +++ b/pioneer/packages/joy-election/src/CandidatePreview.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import AddressMini from '@polkadot/react-components/AddressMiniJoy'; -import MemberByAccount from '@polkadot/joy-utils/MemberByAccountPreview'; +import AddressMini from '@polkadot/react-components/AddressMini'; +import MemberByAccount from '@polkadot/joy-utils/react/components/MemberByAccountPreview'; import { AccountId } from '@polkadot/types/interfaces'; import styled from 'styled-components'; diff --git a/pioneer/packages/joy-election/src/Council.tsx b/pioneer/packages/joy-election/src/Council.tsx index b81c144883..f4e9e9597d 100644 --- a/pioneer/packages/joy-election/src/Council.tsx +++ b/pioneer/packages/joy-election/src/Council.tsx @@ -2,22 +2,22 @@ import React from 'react'; import { ApiProps } from '@polkadot/react-api/types'; import { I18nProps } from '@polkadot/react-components/types'; -import { withCalls } from '@polkadot/react-api/with'; +import { withCalls } from '@polkadot/react-api/hoc'; import { Table } from 'semantic-ui-react'; import { formatBalance } from '@polkadot/util'; import CouncilCandidate from './CandidatePreview'; -import { calcBackersStake } from '@polkadot/joy-utils/index'; +import { calcBackersStake } from '@polkadot/joy-utils/functions/misc'; import { Seat } from '@joystream/types/council'; import translate from './translate'; -import Section from '@polkadot/joy-utils/Section'; +import Section from '@polkadot/joy-utils/react/components/Section'; +import { RouteProps } from 'react-router-dom'; -type Props = ApiProps & -I18nProps & { +type Props = RouteProps & ApiProps & I18nProps & { council?: Seat[]; }; -type State = {}; +type State = Record; class Council extends React.PureComponent { state: State = {}; @@ -53,9 +53,10 @@ class Council extends React.PureComponent { render () { const { council = [] } = this.props; + // console.log({ council }); return ( -
+
{!council.length ? Council is not elected yet : this.renderTable(council)}
); diff --git a/pioneer/packages/joy-election/src/Dashboard.tsx b/pioneer/packages/joy-election/src/Dashboard.tsx index c7f64ef811..da6fffbd9c 100644 --- a/pioneer/packages/joy-election/src/Dashboard.tsx +++ b/pioneer/packages/joy-election/src/Dashboard.tsx @@ -3,18 +3,19 @@ import React from 'react'; import { ApiProps } from '@polkadot/react-api/types'; import { I18nProps } from '@polkadot/react-components/types'; -import { withCalls } from '@polkadot/react-api/with'; +import { withCalls } from '@polkadot/react-api/hoc'; import { Option } from '@polkadot/types'; import { BlockNumber, Balance } from '@polkadot/types/interfaces'; -import { Bubble } from '@polkadot/react-components/index'; +import { Label, Icon } from 'semantic-ui-react'; import { formatNumber, formatBalance } from '@polkadot/util'; -import Section from '@polkadot/joy-utils/Section'; -import { queryToProp } from '@polkadot/joy-utils/index'; +import Section from '@polkadot/joy-utils/react/components/Section'; +import { queryToProp } from '@polkadot/joy-utils/functions/misc'; import { ElectionStage, Seat } from '@joystream/types/council'; import translate from './translate'; +import { RouteProps } from 'react-router-dom'; -type Props = ApiProps & I18nProps & { +type Props = RouteProps & ApiProps & I18nProps & { bestNumber?: BN; activeCouncil?: Seat[]; @@ -34,7 +35,7 @@ type Props = ApiProps & I18nProps & { stage?: Option; }; -type State = {}; +type State = Record; class Dashboard extends React.PureComponent { state: State = {}; @@ -45,12 +46,17 @@ class Dashboard extends React.PureComponent { const title = `Council ${activeCouncil.length > 0 ? '' : '(not elected)'}`; return
- - {activeCouncil.length} - - - {formatNumber(p.termEndsAt)} - + + + +
; } @@ -59,13 +65,16 @@ class Dashboard extends React.PureComponent { let stageName: string | undefined; let stageEndsAt: BlockNumber | undefined; + if (stage && stage.isSome) { const stageValue = stage.value as ElectionStage; + stageEndsAt = stageValue.value as BlockNumber; // contained value stageName = stageValue.type; // name of Enum variant } let leftBlocks: BN | undefined; + if (stageEndsAt && bestNumber) { leftBlocks = stageEndsAt.sub(bestNumber); } @@ -76,20 +85,28 @@ class Dashboard extends React.PureComponent { const title = <>Election ({stateText}); return
- - {formatNumber(round)} - - {isRunning && <> - - {stageName} - - - {formatNumber(leftBlocks)} - - - {formatNumber(stageEndsAt)} - - } + + + {isRunning && <> + + + + } +
; } @@ -98,33 +115,46 @@ class Dashboard extends React.PureComponent { const isAutoStart = (p.autoStart || false).valueOf(); return
- - {isAutoStart ? 'Yes' : 'No'} - - - {formatNumber(p.newTermDuration)} - - - {formatNumber(p.candidacyLimit)} - - - {formatNumber(p.councilSize)} - - - {formatBalance(p.minCouncilStake)} - - - {formatBalance(p.minVotingStake)} - - - {formatNumber(p.announcingPeriod)} blocks - - - {formatNumber(p.votingPeriod)} blocks - - - {formatNumber(p.revealingPeriod)} blocks - + + + + + + + + + + + + +
; } diff --git a/pioneer/packages/joy-election/src/Reveals.tsx b/pioneer/packages/joy-election/src/Reveals.tsx index 6a20dad6f9..7b51d2cb0c 100644 --- a/pioneer/packages/joy-election/src/Reveals.tsx +++ b/pioneer/packages/joy-election/src/Reveals.tsx @@ -1,23 +1,23 @@ import React from 'react'; -import { AppProps, I18nProps } from '@polkadot/react-components/types'; +import { I18nProps } from '@polkadot/react-components/types'; import { ApiProps } from '@polkadot/react-api/types'; -import { withCalls, withMulti } from '@polkadot/react-api/with'; +import { withCalls, withMulti } from '@polkadot/react-api/hoc'; import { AccountId } from '@polkadot/types/interfaces'; import { Input, Labelled, InputAddress } from '@polkadot/react-components/index'; import translate from './translate'; -import { nonEmptyStr, queryToProp, getUrlParam } from '@polkadot/joy-utils/index'; +import { nonEmptyStr, queryToProp, getUrlParam } from '@polkadot/joy-utils/functions/misc'; import { accountIdsToOptions, hashVote } from './utils'; -import TxButton from '@polkadot/joy-utils/TxButton'; +import TxButton from '@polkadot/joy-utils/react/components/TxButton'; import { findVoteByHash } from './myVotesStore'; -import { withOnlyMembers } from '@polkadot/joy-utils/MyAccount'; +import { withOnlyMembers } from '@polkadot/joy-utils/react/hocs/guards'; +import { RouteProps } from 'react-router-dom'; // AppsProps is needed to get a location from the route. -type Props = AppProps & ApiProps & I18nProps & { +type Props = RouteProps & ApiProps & I18nProps & { applicantId?: string | null; applicants?: AccountId[]; - location: any; }; type State = { @@ -30,8 +30,9 @@ class RevealVoteForm extends React.PureComponent { constructor (props: Props) { super(props); let { applicantId, location } = this.props; - applicantId = applicantId || getUrlParam(location, 'applicantId'); - const hashedVote = getUrlParam(location, 'hashedVote'); + + applicantId = applicantId || (location && getUrlParam(location, 'applicantId')); + const hashedVote = location && getUrlParam(location, 'hashedVote'); this.state = { applicantId, @@ -45,6 +46,7 @@ class RevealVoteForm extends React.PureComponent { const applicantOpts = accountIdsToOptions(this.props.applicants || []); const myVote = hashedVote ? findVoteByHash(hashedVote) : undefined; + if (myVote) { // Try to substitue applicantId and salt from local sotre: if (!applicantId) applicantId = myVote.applicantId; @@ -81,15 +83,16 @@ class RevealVoteForm extends React.PureComponent { onChange={this.onChangeSalt} />
} - - - +
+ + + +
); } diff --git a/pioneer/packages/joy-election/src/SealedVote.tsx b/pioneer/packages/joy-election/src/SealedVote.tsx index 54650328c1..769a884e4a 100644 --- a/pioneer/packages/joy-election/src/SealedVote.tsx +++ b/pioneer/packages/joy-election/src/SealedVote.tsx @@ -1,38 +1,47 @@ import React from 'react'; import { Link } from 'react-router-dom'; -import { Table } from 'semantic-ui-react'; +import { Table, Message } from 'semantic-ui-react'; import { I18nProps } from '@polkadot/react-components/types'; import { ApiProps } from '@polkadot/react-api/types'; -import { withCalls } from '@polkadot/react-api/with'; +import { withCalls } from '@polkadot/react-api/hoc'; import { Hash } from '@polkadot/types/interfaces'; import { formatBalance } from '@polkadot/util'; import translate from './translate'; -import { calcTotalStake } from '@polkadot/joy-utils/index'; +import { calcTotalStake } from '@polkadot/joy-utils/functions/misc'; import { SealedVote } from '@joystream/types/council'; -import AddressMini from '@polkadot/react-components/AddressMiniJoy'; +import AddressMini from '@polkadot/react-components/AddressMini'; import CandidatePreview from './CandidatePreview'; import { findVoteByHash } from './myVotesStore'; type Props = ApiProps & I18nProps & { hash: Hash; sealedVote?: SealedVote; + isStageRevealing: boolean; + isMyVote: boolean; }; class Comp extends React.PureComponent { renderCandidateOrAction () { - const { hash, sealedVote } = this.props; + const { hash, sealedVote, isStageRevealing, isMyVote } = this.props; + if (!sealedVote) { return Unknown hashed vote: {hash.toHex()}; } if (sealedVote.vote.isSome) { const candidateId = sealedVote.vote.unwrap(); + return ; - } else { + } else if (isStageRevealing && isMyVote) { const revealUrl = `/council/reveals?hashedVote=${hash.toHex()}`; + return Reveal this vote; + } else if (isMyVote) { + return Wait until Revealing stage in order to reveal this vote.; + } else { + return This vote has not been revealed yet.; } } diff --git a/pioneer/packages/joy-election/src/SealedVotes.tsx b/pioneer/packages/joy-election/src/SealedVotes.tsx index 1f03b66ac0..5effd2b117 100644 --- a/pioneer/packages/joy-election/src/SealedVotes.tsx +++ b/pioneer/packages/joy-election/src/SealedVotes.tsx @@ -1,36 +1,43 @@ import React from 'react'; import { Link } from 'react-router-dom'; -import { Button } from 'semantic-ui-react'; +import { Button, Message } from 'semantic-ui-react'; import { I18nProps } from '@polkadot/react-components/types'; import { ApiProps } from '@polkadot/react-api/types'; -import { withCalls } from '@polkadot/react-api/with'; +import { withCalls } from '@polkadot/react-api/hoc'; import { Hash } from '@polkadot/types/interfaces'; import translate from './translate'; import SealedVote from './SealedVote'; -import { queryToProp } from '@polkadot/joy-utils/index'; -import { MyAddressProps } from '@polkadot/joy-utils/MyAccount'; +import { queryToProp } from '@polkadot/joy-utils/functions/misc'; +import { MyAddressProps } from '@polkadot/joy-utils/react/hocs/accounts'; import { SavedVote } from './myVotesStore'; -import Section from '@polkadot/joy-utils/Section'; +import Section from '@polkadot/joy-utils/react/components/Section'; type Props = ApiProps & I18nProps & MyAddressProps & { myVotes?: SavedVote[]; commitments?: Hash[]; + isStageRevealing: boolean; }; class Comp extends React.PureComponent { private filterVotes = (myVotesOnly: boolean): Hash[] => { const { myVotes = [], commitments = [] } = this.props; + const isMyVote = (hash: string): boolean => { - return myVotes.find(x => x.hash === hash) !== undefined; + return myVotes.find((x) => x.hash === hash) !== undefined; }; - return commitments.filter(x => myVotesOnly === isMyVote(x.toHex())); + + return commitments.filter((x) => myVotesOnly === isMyVote(x.toHex())); } - private renderVotes = (votes: Hash[]) => { + private renderVotes = (votes: Hash[], areVotesMine: boolean) => { return votes.map((hash, index) => - + ); } @@ -39,17 +46,19 @@ class Comp extends React.PureComponent { const otherVotes = this.filterVotes(false); return <> -
{ - !myVotes.length - ? No votes by the current account found on the current browser. - : this.renderVotes(myVotes) - }
+
+ { + !myVotes.length + ? No votes by the current account found on the current browser. + : this.renderVotes(myVotes, true) + } + { this.props.isStageRevealing && } +
- { !otherVotes.length ? No votes submitted by other accounts yet. - : this.renderVotes(otherVotes) + : this.renderVotes(otherVotes, false) }
; diff --git a/pioneer/packages/joy-election/src/SidebarSubtitle.tsx b/pioneer/packages/joy-election/src/SidebarSubtitle.tsx new file mode 100644 index 0000000000..82e63d39bf --- /dev/null +++ b/pioneer/packages/joy-election/src/SidebarSubtitle.tsx @@ -0,0 +1,35 @@ +/** Component providing election stage subtitle for SideBar menu **/ +import React from 'react'; +import { ElectionStage } from '@joystream/types/council'; +import { Option } from '@polkadot/types/codec'; +import { useApi, useCall } from '@polkadot/react-hooks'; +import styled from 'styled-components'; + +const colorByStage = { + Announcing: '#4caf50', + Voting: '#2196f3', + Revealing: '#ff5722' +} as const; + +type StyledSubtitleProps = { + stage?: keyof typeof colorByStage; +} +const StyledSubtitle = styled.div` + display: block; + font-size: 0.85rem; + color: ${(props: StyledSubtitleProps) => props.stage ? colorByStage[props.stage] : 'grey'}; +`; + +export default function SidebarSubtitle () { + const apiProps = useApi(); + const electionStage = useCall>(apiProps.isApiReady && apiProps.api.query.councilElection.stage, []); + + if (electionStage) { + const stageName = electionStage.unwrapOr(undefined)?.type; + const text = stageName ? `${stageName} stage` : 'No active election'; + + return {text}; + } + + return null; +} diff --git a/pioneer/packages/joy-election/src/VoteForm.tsx b/pioneer/packages/joy-election/src/VoteForm.tsx index 63fbb78b17..418c4bd6d0 100644 --- a/pioneer/packages/joy-election/src/VoteForm.tsx +++ b/pioneer/packages/joy-election/src/VoteForm.tsx @@ -4,9 +4,9 @@ import uuid from 'uuid/v4'; import React from 'react'; import { Message, Table } from 'semantic-ui-react'; -import { AppProps, I18nProps } from '@polkadot/react-components/types'; +import { I18nProps } from '@polkadot/react-components/types'; import { ApiProps } from '@polkadot/react-api/types'; -import { withCalls, withMulti } from '@polkadot/react-api/with'; +import { withCalls, withMulti } from '@polkadot/react-api/hoc'; import { AccountId, Balance } from '@polkadot/types/interfaces'; import { Button, Input, Labelled } from '@polkadot/react-components/index'; import { SubmittableResult } from '@polkadot/api'; @@ -14,14 +14,16 @@ import { formatBalance } from '@polkadot/util'; import translate from './translate'; import { hashVote } from './utils'; -import { queryToProp, ZERO, getUrlParam, nonEmptyStr } from '@polkadot/joy-utils/index'; -import TxButton from '@polkadot/joy-utils/TxButton'; -import InputStake from '@polkadot/joy-utils/InputStake'; +import { queryToProp, ZERO, getUrlParam, nonEmptyStr } from '@polkadot/joy-utils/functions/misc'; +import TxButton from '@polkadot/joy-utils/react/components/TxButton'; +import InputStake from '@polkadot/joy-utils/react/components/InputStake'; import CandidatePreview from './CandidatePreview'; -import { MyAccountProps, withOnlyMembers } from '@polkadot/joy-utils/MyAccount'; -import MembersDropdown from '@polkadot/joy-utils/MembersDropdown'; +import { MyAccountProps } from '@polkadot/joy-utils/react/hocs/accounts'; +import { withOnlyMembers } from '@polkadot/joy-utils/react/hocs/guards'; +import MembersDropdown from '@polkadot/joy-utils/react/components/MembersDropdown'; import { saveVote, NewVote } from './myVotesStore'; import { TxFailedCallback } from '@polkadot/react-components/Status/types'; +import { RouteProps } from 'react-router-dom'; // TODO use a crypto-prooven generator instead of UUID 4. function randomSalt () { @@ -29,11 +31,10 @@ function randomSalt () { } // AppsProps is needed to get a location from the route. -type Props = AppProps & ApiProps & I18nProps & MyAccountProps & { +type Props = RouteProps & ApiProps & I18nProps & MyAccountProps & { applicantId?: string | null; minVotingStake?: Balance; applicants?: AccountId[]; - location?: any; }; type State = { @@ -49,7 +50,8 @@ class Component extends React.PureComponent { super(props); let { applicantId, location } = this.props; - applicantId = applicantId || getUrlParam(location, 'applicantId'); + + applicantId = applicantId || (location && getUrlParam(location, 'applicantId')); this.state = { applicantId, @@ -103,14 +105,11 @@ class Component extends React.PureComponent { - - + - + { proposalsBatch && (<> { proposalsBatch.totalBatches > 1 && ( @@ -81,16 +81,16 @@ function ProposalPreviewList ({ bestNumber }: ProposalPreviewListProps) { /> ) } - { proposalsBatch.proposals.length - ? ( - - {proposalsBatch.proposals.map((prop: ParsedProposal, idx: number) => ( - - ))} - - ) - : `There are currently no ${activeFilter !== 'All' ? activeFilter.toLocaleLowerCase() : 'submitted'} proposals.` - } + { proposalsBatch.proposals.length + ? ( + + {proposalsBatch.proposals.map((prop: ParsedProposal, idx: number) => ( + + ))} + + ) + : `There are currently no ${activeFilter !== 'All' ? activeFilter.toLocaleLowerCase() : 'submitted'} proposals.` + } ) } diff --git a/pioneer/packages/joy-proposals/src/Proposal/ProposalTypePreview.tsx b/pioneer/packages/joy-proposals/src/Proposal/ProposalTypePreview.tsx index 092848a77b..994faf34bf 100644 --- a/pioneer/packages/joy-proposals/src/Proposal/ProposalTypePreview.tsx +++ b/pioneer/packages/joy-proposals/src/Proposal/ProposalTypePreview.tsx @@ -6,7 +6,7 @@ import { Item, Icon, Button, Label } from 'semantic-ui-react'; import { ProposalType, Category } from '@polkadot/joy-utils/types/proposals'; import _ from 'lodash'; import styled from 'styled-components'; -import useVoteStyles from './useVoteStyles'; +import getVoteStyles from './getVoteStyles'; import { formatBalance } from '@polkadot/util'; import './ProposalType.css'; @@ -62,9 +62,9 @@ type ProposalTypePreviewProps = { }; const ProposalTypeDetail = (props: { title: string; value: string }) => ( -
-
{ `${props.title}:` }
-
{ props.value }
+
+
{ `${props.title}:` }
+
{ props.value }
); @@ -90,7 +90,7 @@ export default function ProposalTypePreview (props: ProposalTypePreviewProps) { }; return ( - + {/* TODO: We can add it once we have the actual assets @@ -98,51 +98,51 @@ export default function ProposalTypePreview (props: ProposalTypePreviewProps) { {_.startCase(type)} {description} -
+
1 ? 's' : ''}` : 'NONE' } /> 1 ? 's' : ''}` : 'NONE' } />
{ approvalQuorum && ( - - + + Approval Quorum: { approvalQuorum }% ) } { approvalThreshold && ( - - + + Approval Threshold: { approvalThreshold }% ) } { slashingQuorum && ( - - + + Slashing Quorum: { slashingQuorum }% ) } { slashingThreshold && ( - - + + Slashing Threshold: { slashingThreshold }% ) } -
- +
+ Create - +
diff --git a/pioneer/packages/joy-proposals/src/Proposal/Votes.tsx b/pioneer/packages/joy-proposals/src/Proposal/Votes.tsx index 6eec0b42c8..20cea5011d 100644 --- a/pioneer/packages/joy-proposals/src/Proposal/Votes.tsx +++ b/pioneer/packages/joy-proposals/src/Proposal/Votes.tsx @@ -1,12 +1,12 @@ import React from 'react'; import { Header, Divider, Table, Icon } from 'semantic-ui-react'; -import useVoteStyles from './useVoteStyles'; +import getVoteStyles from './getVoteStyles'; import { VoteKind } from '@joystream/types/proposals'; import { VoteKindStr } from './VotingSection'; -import ProfilePreview from '@polkadot/joy-utils/MemberProfilePreview'; +import ProfilePreview from '@polkadot/joy-utils/react/components/MemberProfilePreview'; import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks'; import { ParsedProposal, ProposalVotes } from '@polkadot/joy-utils/types/proposals'; -import { PromiseComponent } from '@polkadot/joy-utils/react/components'; +import PromiseComponent from '@polkadot/joy-utils/react/components/PromiseComponent'; type VotesProps = { proposal: ParsedProposal; @@ -24,20 +24,21 @@ export default function Votes ({ proposal: { id, votingResults } }: VotesProps) + message='Fetching the votes...'> { (votes && votes.votes.length > 0) ? ( <> -
+
All Votes: ({votes.votes.length}/{votes.councilMembersLength})
- +
{votes.votes.map((proposalVote, idx) => { const { vote, member } = proposalVote; const voteStr = (vote as VoteKind).type.toString() as VoteKindStr; - const { icon, textColor } = useVoteStyles(voteStr); + const { icon, textColor } = getVoteStyles(voteStr); + return ( @@ -59,7 +60,7 @@ export default function Votes ({ proposal: { id, votingResults } }: VotesProps) ) : ( -
No votes have been submitted!
+
No votes have been submitted!
) } diff --git a/pioneer/packages/joy-proposals/src/Proposal/VotingSection.tsx b/pioneer/packages/joy-proposals/src/Proposal/VotingSection.tsx index 7e10d4b1fb..2908792788 100644 --- a/pioneer/packages/joy-proposals/src/Proposal/VotingSection.tsx +++ b/pioneer/packages/joy-proposals/src/Proposal/VotingSection.tsx @@ -1,8 +1,8 @@ import React, { useState } from 'react'; -import { Icon, Button, Message, Divider, Header } from 'semantic-ui-react'; -import useVoteStyles from './useVoteStyles'; -import TxButton from '@polkadot/joy-utils/TxButton'; +import { Icon, Message, Divider, Header } from 'semantic-ui-react'; +import getVoteStyles from './getVoteStyles'; +import { SemanticTxButton } from '@polkadot/joy-utils/react/components/TxButton'; import { MemberId } from '@joystream/types/members'; import { ProposalId, VoteKind, VoteKinds } from '@joystream/types/proposals'; import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks'; @@ -32,27 +32,28 @@ type VoteButtonProps = { proposalId: ProposalId; onSuccess: () => void; } + function VoteButton ({ voteKind, proposalId, memberId, onSuccess }: VoteButtonProps) { - const { icon, color } = useVoteStyles(voteKind); + const { icon, color } = getVoteStyles(voteKind); + return ( - // Button.Group "cheat" to force TxButton color - - sendTx() } - txFailedCb={ () => null } - txSuccessCb={ onSuccess } - className={'icon left labeled'}> - - { voteKind } - - + sendTx() } + txFailedCb={ () => null } + txSuccessCb={ onSuccess } + color={color} + style={{ marginRight: '5px' }} + icon + labelPosition={ 'left' }> + + { voteKind } + ); } @@ -82,13 +83,13 @@ export default function VotingSection ({ const voteStr: VoteKindStr | null = voted || (vote && vote.type.toString() as VoteKindStr); if (voteStr) { - const { icon, color } = useVoteStyles(voteStr); + const { icon, color } = getVoteStyles(voteStr); return ( - You voted {`"${voteStr}"`} + You voted {`"${voteStr}"`} ); @@ -98,7 +99,7 @@ export default function VotingSection ({ return ( <> -
Sumbit your vote
+
Sumbit your vote
{ VoteKinds.map((voteKind) => diff --git a/pioneer/packages/joy-proposals/src/Proposal/discussion/DiscussionPost.tsx b/pioneer/packages/joy-proposals/src/Proposal/discussion/DiscussionPost.tsx index 40d3125e22..b1c26fdea5 100644 --- a/pioneer/packages/joy-proposals/src/Proposal/discussion/DiscussionPost.tsx +++ b/pioneer/packages/joy-proposals/src/Proposal/discussion/DiscussionPost.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { Button, Icon } from 'semantic-ui-react'; import { ParsedPost } from '@polkadot/joy-utils/types/proposals'; -import MemberProfilePreview from '@polkadot/joy-utils/MemberProfilePreview'; +import MemberProfilePreview from '@polkadot/joy-utils/react/components/MemberProfilePreview'; import DiscussionPostForm from './DiscussionPostForm'; import { MemberId } from '@joystream/types/members'; import { useTransport } from '@polkadot/joy-utils/react/hooks'; @@ -61,6 +61,7 @@ export default function DiscussionPost ({ authorId.toNumber() === memberId.toNumber() && editsCount < constraints.maxPostEdits ); + const onEditSuccess = () => { setEditing(false); refreshDiscussion(); @@ -98,9 +99,9 @@ export default function DiscussionPost ({ setEditing(true)} primary - size="tiny" + size='tiny' icon> - + ) } diff --git a/pioneer/packages/joy-proposals/src/Proposal/discussion/DiscussionPostForm.tsx b/pioneer/packages/joy-proposals/src/Proposal/discussion/DiscussionPostForm.tsx index 5b7ff204fd..b9161990bb 100644 --- a/pioneer/packages/joy-proposals/src/Proposal/discussion/DiscussionPostForm.tsx +++ b/pioneer/packages/joy-proposals/src/Proposal/discussion/DiscussionPostForm.tsx @@ -2,8 +2,8 @@ import React from 'react'; import { Form, Field, withFormik, FormikProps } from 'formik'; import * as Yup from 'yup'; -import TxButton from '@polkadot/joy-utils/TxButton'; -import * as JoyForms from '@polkadot/joy-utils/forms'; +import TxButton from '@polkadot/joy-utils/react/components/TxButton'; +import * as JoyForms from '@polkadot/joy-utils/react/components/forms'; import { SubmittableResult } from '@polkadot/api'; import { Button } from 'semantic-ui-react'; import { TxFailedCallback, TxCallback } from '@polkadot/react-components/Status/types'; @@ -76,7 +76,7 @@ const DiscussionPostFormInner = (props: InnerProps) => { }; return ( -
+ { { /> { isEditForm ? ( )} - - + ); }; diff --git a/pioneer/packages/joy-proposals/src/forms/GenericWorkingGroupProposalForm.tsx b/pioneer/packages/joy-proposals/src/forms/GenericWorkingGroupProposalForm.tsx index 4c228e15dd..98bc9dfaa3 100644 --- a/pioneer/packages/joy-proposals/src/forms/GenericWorkingGroupProposalForm.tsx +++ b/pioneer/packages/joy-proposals/src/forms/GenericWorkingGroupProposalForm.tsx @@ -1,20 +1,17 @@ import React from 'react'; import { getFormErrorLabelsProps } from './errorHandling'; -import { - GenericProposalForm, +import { GenericProposalForm, GenericFormValues, genericFormDefaultValues, ProposalFormExportProps, ProposalFormContainerProps, - ProposalFormInnerProps -} from './GenericProposalForm'; + ProposalFormInnerProps } from './GenericProposalForm'; import { FormField } from './FormFields'; import { ProposalType } from '@polkadot/joy-utils/types/proposals'; import { WorkingGroupKey, WorkingGroupDef } from '@joystream/types/common'; -import './forms.css'; import { Dropdown, Message } from 'semantic-ui-react'; import { usePromise, useTransport } from '@polkadot/joy-utils/react/hooks'; -import { PromiseComponent } from '@polkadot/joy-utils/react/components'; +import PromiseComponent from '@polkadot/joy-utils/react/components/PromiseComponent'; import { WorkerData } from '@polkadot/joy-utils/types/workingGroups'; import { LeadInfo } from '@polkadot/joy-utils/react/components/working-groups/LeadInfo'; @@ -46,7 +43,7 @@ type ExportComponentProps = ProposalFormExportProps; export type FormInnerProps = ProposalFormInnerProps; -export const GenericWorkingGroupProposalForm: React.FunctionComponent = props => { +export const GenericWorkingGroupProposalForm: React.FunctionComponent = (props) => { const { handleChange, errors, @@ -70,20 +67,21 @@ export const GenericWorkingGroupProposalForm: React.FunctionComponent(errors, touched); + return ( ({ text: wgKey + ' Working Group', value: wgKey }))} + options={Object.keys(WorkingGroupDef).map((wgKey) => ({ text: wgKey + ' Working Group', value: wgKey }))} value={values.workingGroup} onChange={ handleChange } /> diff --git a/pioneer/packages/joy-proposals/src/forms/LabelWithHelp.tsx b/pioneer/packages/joy-proposals/src/forms/LabelWithHelp.tsx index d8880ba5bc..deb9be8c0d 100644 --- a/pioneer/packages/joy-proposals/src/forms/LabelWithHelp.tsx +++ b/pioneer/packages/joy-proposals/src/forms/LabelWithHelp.tsx @@ -5,6 +5,7 @@ type LabelWithHelpProps = { text: string; help: string }; export default function LabelWithHelp (props: LabelWithHelpProps) { const [open, setOpen] = useState(false); + return (