diff --git a/.gitignore b/.gitignore index b3e42e3d38..b43c8bb717 100755 --- a/.gitignore +++ b/.gitignore @@ -86,6 +86,10 @@ mainnet-genesis-dryrun-with-stakeholders.json # nix-build results result* +!result*.js +!result*.jsx +!result*.ts +!result*.tsx # Npm package-lock.json diff --git a/source/renderer/app/components/staking/delegation-center/WalletRow.js b/source/renderer/app/components/staking/delegation-center/WalletRow.js index 4e732a3acf..98927a2cd3 100644 --- a/source/renderer/app/components/staking/delegation-center/WalletRow.js +++ b/source/renderer/app/components/staking/delegation-center/WalletRow.js @@ -82,22 +82,16 @@ const messages = defineMessages({ type Props = { wallet: Wallet, delegatedStakePool?: ?StakePool, - numberOfStakePools: number, numberOfRankedStakePools: number, onDelegate: Function, onUndelegate: Function, getStakePoolById: Function, nextEpochNumber: number, futureEpochNumber: number, - onSelect?: Function, - selectedPoolId?: ?number, - isListActive?: boolean, currentTheme: string, onOpenExternalLink: Function, showWithSelectButton?: boolean, containerClassName: string, - setListActive?: Function, - listName?: string, }; type WalletRowState = { diff --git a/source/renderer/app/components/voting/VotingUnavailable.js b/source/renderer/app/components/voting/VotingUnavailable.js index 98dbb343b4..7cc3c63392 100644 --- a/source/renderer/app/components/voting/VotingUnavailable.js +++ b/source/renderer/app/components/voting/VotingUnavailable.js @@ -1,132 +1,30 @@ // @flow -import React, { Component } from 'react'; +import React from 'react'; import { observer } from 'mobx-react'; -import { - defineMessages, - intlShape, - FormattedMessage, - FormattedHTMLMessage, -} from 'react-intl'; -import BigNumber from 'bignumber.js'; -import { Link } from 'react-polymorph/lib/components/Link'; +import { FormattedHTMLMessage } from 'react-intl'; import globalMessages from '../../i18n/global-messages'; import LoadingSpinner from '../widgets/LoadingSpinner'; -import { - CURRENT_VOTING_FUND_NUMBER, - NEXT_VOTING_FUND_NUMBER, -} from '../../config/votingConfig'; import styles from './VotingUnavailable.scss'; - -const messages = defineMessages({ - heading: { - id: 'voting.unavailable.heading', - defaultMessage: '!!!Project Catalyst voting registration', - description: 'Headline for the "Voting unavailable" screen', - }, - paragraph1: { - id: 'voting.unavailable.paragraph1', - defaultMessage: - '!!!Project Catalyst Fund{currentVotingFundNumber} has now ended. Fund{nextVotingFundNumber} is currently in preparation, voting registration is not available yet.', - description: 'First paragraph on the "Voting unavailable" screen', - }, - paragraph2: { - id: 'voting.unavailable.paragraph2', - defaultMessage: - '!!!Join our {link1} and {link2} Telegram channels for the latest updates (English language only).', - description: 'Second paragraph on the "Voting unavailable" screen', - }, - link1Text: { - id: 'voting.unavailable.link1Text', - defaultMessage: '!!!Catalyst Announcements', - description: 'First link text on the "Voting unavailable" screen', - }, - link2Text: { - id: 'voting.unavailable.link2Text', - defaultMessage: '!!!Project Catalyst Chat', - description: 'Second link text on the "Voting unavailable" screen', - }, - link1Url: { - id: 'voting.unavailable.link1Url', - defaultMessage: '!!!https://t.me/cardanocatalyst', - description: 'First link URL on the "Voting unavailable" screen', - }, - link2Url: { - id: 'voting.unavailable.link2Url', - defaultMessage: '!!!https://t.me/ProjectCatalystChat', - description: 'Second link URL on the "Voting unavailable" screen', - }, -}); +import { formattedNumber } from '../../utils/formatters'; type Props = { syncPercentage: number, - isVotingRegistrationAvailable: boolean, - onExternalLinkClick: Function, }; -@observer -export default class VotingUnavailable extends Component { - static contextTypes = { - intl: intlShape.isRequired, - }; - - render() { - const { intl } = this.context; - const { - syncPercentage, - isVotingRegistrationAvailable, - onExternalLinkClick, - } = this.props; - - const heading = intl.formatMessage(messages.heading); - const paragraph1 = intl.formatMessage(messages.paragraph1, { - currentFundNumber: CURRENT_VOTING_FUND_NUMBER, - nextVotingFundNumber: NEXT_VOTING_FUND_NUMBER, - }); - const link1 = ( - - onExternalLinkClick(intl.formatMessage(messages.link1Url)) - } - /> - ); - const link2 = ( - - onExternalLinkClick(intl.formatMessage(messages.link2Url)) - } - /> - ); - - return ( -
- {isVotingRegistrationAvailable ? ( - <> - -
- -
- - ) : ( - <> -

{heading}

-

{paragraph1}

- - - )} +const VotingUnavailable = ({ syncPercentage }: Props) => { + return ( +
+ +
+
- ); - } -} +
+ ); +}; + +export default observer(VotingUnavailable); diff --git a/source/renderer/app/components/voting/voting-info/CurrentFund.js b/source/renderer/app/components/voting/voting-info/CurrentFund.js deleted file mode 100644 index c0053a628b..0000000000 --- a/source/renderer/app/components/voting/voting-info/CurrentFund.js +++ /dev/null @@ -1,70 +0,0 @@ -// @flow -import React from 'react'; -import { injectIntl } from 'react-intl'; -import { - VOTING_REGISTRATION_CAST_END_DATE, - CURRENT_VOTING_FUND_NUMBER, -} from '../../../config/votingConfig'; -import { - formattedDateTime, - mapToLongDateTimeFormat, -} from '../../../utils/formatters'; -import type { Locale } from '../../../../../common/types/locales.types'; -import { ExternalLinkButton } from '../../widgets/ExternalLinkButton'; -import type { Intl } from '../../../types/i18nTypes'; -import styles from './CurrentFund.scss'; -import { messages } from './CurrentFund.messages'; - -type Props = { - currentLocale: Locale, - currentDateFormat: string, - currentTimeFormat: string, - onExternalLinkClick: Function, - intl: Intl, -}; - -function CurrentFund({ - currentLocale, - currentDateFormat, - currentTimeFormat, - onExternalLinkClick, - intl, -}: Props) { - const currentFundEndDate = formattedDateTime( - VOTING_REGISTRATION_CAST_END_DATE, - { - currentLocale, - ...mapToLongDateTimeFormat({ - currentLocale, - currentDateFormat, - currentTimeFormat, - }), - } - ); - - return ( -
-

- {intl.formatMessage(messages.name, { - currentVotingFundNumber: CURRENT_VOTING_FUND_NUMBER, - })} -

- -
- - {intl.formatMessage(messages.headingForEndDate)} - - {currentFundEndDate} -
- - - onExternalLinkClick(intl.formatMessage(messages.viewResultsLinkURL)) - } - /> -
- ); -} - -export default injectIntl(CurrentFund); diff --git a/source/renderer/app/components/voting/voting-info/CurrentFund.messages.js b/source/renderer/app/components/voting/voting-info/CurrentFund.messages.js deleted file mode 100644 index cef12e1c44..0000000000 --- a/source/renderer/app/components/voting/voting-info/CurrentFund.messages.js +++ /dev/null @@ -1,25 +0,0 @@ -// @flow -import { defineMessages } from 'react-intl'; - -export const messages = defineMessages({ - name: { - id: 'voting.currentFund.name', - defaultMessage: '!!!Fund{currentVotingFundNumber}', - description: 'Current fund name', - }, - headingForEndDate: { - id: 'voting.currentFund.headingForEndDate', - defaultMessage: '!!!End of voting:', - description: 'Headline for end date', - }, - viewResultsLinkLabel: { - id: 'voting.currentFund.viewResultsLinkLabel', - defaultMessage: '!!!View results', - description: 'View resuls link label for Fund{currentVotingFundNumber}', - }, - viewResultsLinkURL: { - id: 'voting.currentFund.viewResultsLinkURL', - defaultMessage: 'https://cardano.ideascale.com/a/pages/results', - description: 'View results from Fund{currentVotingFundNumber}', - }, -}); diff --git a/source/renderer/app/components/voting/voting-info/CurrentFund.scss b/source/renderer/app/components/voting/voting-info/CurrentPhase.scss similarity index 79% rename from source/renderer/app/components/voting/voting-info/CurrentFund.scss rename to source/renderer/app/components/voting/voting-info/CurrentPhase.scss index 731691c221..1d90208480 100644 --- a/source/renderer/app/components/voting/voting-info/CurrentFund.scss +++ b/source/renderer/app/components/voting/voting-info/CurrentPhase.scss @@ -1,13 +1,12 @@ @import '../votingConfig'; -.component { +.root { @extend %regularText; align-items: start; background-color: var(--theme-button-flat-background-color); border-radius: 5px; display: flex; flex-direction: column; - height: 175px; padding: 20px; width: 271px; @@ -16,23 +15,30 @@ font-family: var(--font-bold); font-size: 18px; line-height: 1.33; + margin-bottom: 11px; } - .endDateBlock { + .block { align-items: start; display: flex; flex-direction: column; - margin-bottom: 20px; - margin-top: 11px; - .endDateText { + .label { color: var(--theme-button-flat-text-color); display: block; } - .endDate { + .value { color: var(--theme-button-flat-text-color); font-family: var(--font-medium); } + + & + .block { + margin-top: 11px; + } + } + + .resultsButton { + margin-top: 20px; } } diff --git a/source/renderer/app/components/voting/voting-info/RegisterToVote.js b/source/renderer/app/components/voting/voting-info/RegisterToVote.js index fa14a6cc1d..d0109c2cf4 100644 --- a/source/renderer/app/components/voting/voting-info/RegisterToVote.js +++ b/source/renderer/app/components/voting/voting-info/RegisterToVote.js @@ -4,7 +4,7 @@ import { injectIntl } from 'react-intl'; import { Button } from 'react-polymorph/lib/components/Button'; import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; import { - VOTING_REGISTRATION_END_DATE, + VOTING_NEW_SNAPSHOT_DATE, NEXT_VOTING_FUND_NUMBER, } from '../../../config/votingConfig'; import { @@ -13,8 +13,10 @@ import { } from '../../../utils/formatters'; import type { Locale } from '../../../../../common/types/locales.types'; import type { Intl } from '../../../types/i18nTypes'; -import styles from './RegisterToVote.scss'; import { messages } from './RegisterToVote.messages'; +import { messages as votingMessages } from './VotingInfo.messages'; +import styles from './RegisterToVote.scss'; +import votingStyles from './VotingInfo.scss'; type Props = { currentLocale: Locale, @@ -34,7 +36,7 @@ function RegisterToVote({ const [step1, setStep1] = useState(false); const [step2, setStep2] = useState(false); const canRegister = step1 && step2; - const castEndDate = formattedDateTime(VOTING_REGISTRATION_END_DATE, { + const castEndDate = formattedDateTime(VOTING_NEW_SNAPSHOT_DATE, { currentLocale, ...mapToLongDateTimeFormat({ currentLocale, @@ -46,15 +48,15 @@ function RegisterToVote({ return (
- {intl.formatMessage(messages.name, { - nextVotingFundNumber: NEXT_VOTING_FUND_NUMBER, + {intl.formatMessage(votingMessages.fundName, { + votingFundNumber: NEXT_VOTING_FUND_NUMBER, })} {intl.formatMessage(messages.dateLabel)} {castEndDate} -
+
{intl.formatMessage(messages.stepsTitle)} diff --git a/source/renderer/app/components/voting/voting-info/RegisterToVote.messages.js b/source/renderer/app/components/voting/voting-info/RegisterToVote.messages.js index 0583a97544..bd46ec2849 100644 --- a/source/renderer/app/components/voting/voting-info/RegisterToVote.messages.js +++ b/source/renderer/app/components/voting/voting-info/RegisterToVote.messages.js @@ -2,11 +2,6 @@ import { defineMessages } from 'react-intl'; export const messages = defineMessages({ - name: { - id: 'voting.registerToVote.name', - defaultMessage: '!!!Fund{nextVotingFundNumber}', - description: 'Regiter to fund name', - }, dateLabel: { id: 'voting.registerToVote.dateLabel', defaultMessage: '!!!Snapshot date:', diff --git a/source/renderer/app/components/voting/voting-info/RegisterToVote.scss b/source/renderer/app/components/voting/voting-info/RegisterToVote.scss index b8bed5157f..dbdd57b02f 100644 --- a/source/renderer/app/components/voting/voting-info/RegisterToVote.scss +++ b/source/renderer/app/components/voting/voting-info/RegisterToVote.scss @@ -22,13 +22,6 @@ font-family: var(--font-medium); } - .separator { - background-color: var(--theme-voting-separator-color); - height: 1px; - margin: 20px 0; - width: 100%; - } - .stepsTitle { font-family: var(--font-medium); } diff --git a/source/renderer/app/components/voting/voting-info/ResultsPhase.js b/source/renderer/app/components/voting/voting-info/ResultsPhase.js new file mode 100644 index 0000000000..23a73778ae --- /dev/null +++ b/source/renderer/app/components/voting/voting-info/ResultsPhase.js @@ -0,0 +1,74 @@ +// @flow +import React from 'react'; +import { observer } from 'mobx-react'; +import { injectIntl } from 'react-intl'; +import { + VOTING_CAST_END_DATE, + CURRENT_VOTING_FUND_NUMBER, +} from '../../../config/votingConfig'; +import { + formattedDateTime, + mapToLongDateTimeFormat, +} from '../../../utils/formatters'; +import type { Locale } from '../../../../../common/types/locales.types'; +import { ExternalLinkButton } from '../../widgets/ExternalLinkButton'; +import type { Intl } from '../../../types/i18nTypes'; +import { messages } from './ResultsPhase.messages'; +import { messages as votingMessages } from './VotingInfo.messages'; +import styles from './CurrentPhase.scss'; + +type Props = { + currentLocale: Locale, + currentDateFormat: string, + currentTimeFormat: string, + onExternalLinkClick: Function, + intl: Intl, +}; + +function ResultsPhase({ + currentLocale, + currentDateFormat, + currentTimeFormat, + onExternalLinkClick, + intl, +}: Props) { + const mappedFormats = mapToLongDateTimeFormat({ + currentLocale, + currentDateFormat, + currentTimeFormat, + }); + + const endDate = formattedDateTime(VOTING_CAST_END_DATE, { + currentLocale, + currentDateFormat: mappedFormats.currentDateFormat, + currentTimeFormat: mappedFormats.currentTimeFormat, + }); + + return ( +
+

+ {intl.formatMessage(votingMessages.fundName, { + votingFundNumber: CURRENT_VOTING_FUND_NUMBER, + })} +

+ +
+ + {intl.formatMessage(messages.endDateLabel)} + + {endDate} +
+ +
+ + onExternalLinkClick(intl.formatMessage(messages.viewResultsLinkURL)) + } + /> +
+
+ ); +} + +export default injectIntl(observer(ResultsPhase)); diff --git a/source/renderer/app/components/voting/voting-info/ResultsPhase.messages.js b/source/renderer/app/components/voting/voting-info/ResultsPhase.messages.js new file mode 100644 index 0000000000..bf72163c2c --- /dev/null +++ b/source/renderer/app/components/voting/voting-info/ResultsPhase.messages.js @@ -0,0 +1,20 @@ +// @flow +import { defineMessages } from 'react-intl'; + +export const messages = defineMessages({ + endDateLabel: { + id: 'voting.resultsPhase.endDateLabel', + defaultMessage: '!!!End of voting:', + description: 'Headline for end date', + }, + viewResultsLinkLabel: { + id: 'voting.resultsPhase.viewResultsLinkLabel', + defaultMessage: '!!!View results', + description: 'View results link label', + }, + viewResultsLinkURL: { + id: 'voting.resultsPhase.viewResultsLinkURL', + defaultMessage: 'https://cardano.ideascale.com/a/pages/results', + description: 'View results link', + }, +}); diff --git a/source/renderer/app/components/voting/voting-info/SnapshotPhase.js b/source/renderer/app/components/voting/voting-info/SnapshotPhase.js new file mode 100644 index 0000000000..d10fb7f961 --- /dev/null +++ b/source/renderer/app/components/voting/voting-info/SnapshotPhase.js @@ -0,0 +1,81 @@ +// @flow +import React from 'react'; +import { observer } from 'mobx-react'; +import { injectIntl } from 'react-intl'; +import { + CURRENT_VOTING_FUND_NUMBER, + VOTING_SNAPSHOT_DATE, + VOTING_CAST_START_DATE, + VOTING_CAST_END_DATE, +} from '../../../config/votingConfig'; +import { + formattedDateTime, + mapToLongDateTimeFormat, +} from '../../../utils/formatters'; +import type { Locale } from '../../../../../common/types/locales.types'; +import type { Intl } from '../../../types/i18nTypes'; +import styles from './CurrentPhase.scss'; +import { messages } from './SnapshotPhase.messages'; +import { messages as votingMessages } from './VotingInfo.messages'; + +type Props = { + currentLocale: Locale, + currentDateFormat: string, + currentTimeFormat: string, + intl: Intl, +}; + +function SnapshotPhase({ + currentLocale, + currentDateFormat, + currentTimeFormat, + intl, +}: Props) { + const mappedFormats = mapToLongDateTimeFormat({ + currentLocale, + currentDateFormat, + currentTimeFormat, + }); + + const snapshotDate = formattedDateTime(VOTING_SNAPSHOT_DATE, { + currentLocale, + currentDateFormat: mappedFormats.currentDateFormat, + currentTimeFormat: mappedFormats.currentTimeFormat, + }); + + const startDate = formattedDateTime(VOTING_CAST_START_DATE, { + currentLocale, + currentDateFormat: mappedFormats.currentDateFormat, + }); + + const endDate = formattedDateTime(VOTING_CAST_END_DATE, { + currentLocale, + currentDateFormat: mappedFormats.currentDateFormat, + }); + + return ( +
+

+ {intl.formatMessage(votingMessages.fundName, { + votingFundNumber: CURRENT_VOTING_FUND_NUMBER, + })} +

+
+ + {intl.formatMessage(messages.snapshotDateLabel)} + + {snapshotDate} +
+
+ + {intl.formatMessage(messages.votingDateLabel)} + + + {startDate} – {endDate} + +
+
+ ); +} + +export default injectIntl(observer(SnapshotPhase)); diff --git a/source/renderer/app/components/voting/voting-info/SnapshotPhase.messages.js b/source/renderer/app/components/voting/voting-info/SnapshotPhase.messages.js new file mode 100644 index 0000000000..4eee34d671 --- /dev/null +++ b/source/renderer/app/components/voting/voting-info/SnapshotPhase.messages.js @@ -0,0 +1,15 @@ +// @flow +import { defineMessages } from 'react-intl'; + +export const messages = defineMessages({ + snapshotDateLabel: { + id: 'voting.snapshotPhase.snapshotDateLabel', + defaultMessage: '!!!Snapshot date:', + description: 'Snapshot date label', + }, + votingDateLabel: { + id: 'voting.snapshotPhase.votingDateLabel', + defaultMessage: '!!!Next voting period:', + description: 'Next voting date label', + }, +}); diff --git a/source/renderer/app/components/voting/voting-info/TallyingPhase.js b/source/renderer/app/components/voting/voting-info/TallyingPhase.js new file mode 100644 index 0000000000..ac9dcdda63 --- /dev/null +++ b/source/renderer/app/components/voting/voting-info/TallyingPhase.js @@ -0,0 +1,72 @@ +// @flow +import React from 'react'; +import { observer } from 'mobx-react'; +import { injectIntl } from 'react-intl'; +import { + CURRENT_VOTING_FUND_NUMBER, + VOTING_RESULTS_DATE, + VOTING_CAST_END_DATE, +} from '../../../config/votingConfig'; +import { + formattedDateTime, + mapToLongDateTimeFormat, +} from '../../../utils/formatters'; +import type { Locale } from '../../../../../common/types/locales.types'; +import type { Intl } from '../../../types/i18nTypes'; +import styles from './CurrentPhase.scss'; +import { messages } from './TallyingPhase.messages'; +import { messages as votingMessages } from './VotingInfo.messages'; + +type Props = { + currentLocale: Locale, + currentDateFormat: string, + currentTimeFormat: string, + intl: Intl, +}; + +function TallyingPhase({ + currentLocale, + currentDateFormat, + currentTimeFormat, + intl, +}: Props) { + const mappedFormats = mapToLongDateTimeFormat({ + currentLocale, + currentDateFormat, + currentTimeFormat, + }); + + const endDated = formattedDateTime(VOTING_CAST_END_DATE, { + currentLocale, + currentDateFormat: mappedFormats.currentDateFormat, + }); + + const resultsDate = formattedDateTime(VOTING_RESULTS_DATE, { + currentLocale, + currentDateFormat: mappedFormats.currentDateFormat, + }); + + return ( +
+

+ {intl.formatMessage(votingMessages.fundName, { + votingFundNumber: CURRENT_VOTING_FUND_NUMBER, + })} +

+
+ + {intl.formatMessage(messages.endDateLabel)} + + {endDated} +
+
+ + {intl.formatMessage(messages.resultsLabel)} + + {resultsDate} +
+
+ ); +} + +export default injectIntl(observer(TallyingPhase)); diff --git a/source/renderer/app/components/voting/voting-info/TallyingPhase.messages.js b/source/renderer/app/components/voting/voting-info/TallyingPhase.messages.js new file mode 100644 index 0000000000..64830d7959 --- /dev/null +++ b/source/renderer/app/components/voting/voting-info/TallyingPhase.messages.js @@ -0,0 +1,15 @@ +// @flow +import { defineMessages } from 'react-intl'; + +export const messages = defineMessages({ + endDateLabel: { + id: 'voting.tallyingPhase.endDateLabel', + defaultMessage: '!!!Voting ended:', + description: 'Voting end date label', + }, + resultsLabel: { + id: 'voting.tallyingPhase.resultsLabel', + defaultMessage: '!!!Check back for results on:', + description: 'Results date label', + }, +}); diff --git a/source/renderer/app/components/voting/voting-info/VotingInfo.js b/source/renderer/app/components/voting/voting-info/VotingInfo.js index 43f317cf58..6d10bcc0ec 100644 --- a/source/renderer/app/components/voting/voting-info/VotingInfo.js +++ b/source/renderer/app/components/voting/voting-info/VotingInfo.js @@ -1,69 +1,77 @@ // @flow -import React, { Component } from 'react'; +import React from 'react'; import { observer } from 'mobx-react'; -import { intlShape } from 'react-intl'; import BorderedBox from '../../widgets/BorderedBox'; import type { Locale } from '../../../../../common/types/locales.types'; import styles from './VotingInfo.scss'; -import CurrentFund from './CurrentFund'; +import ResultsPhase from './ResultsPhase'; +import SnapshotPhase from './SnapshotPhase'; +import VotingPhase from './VotingPhase'; +import TallyingPhase from './TallyingPhase'; import Headline from './Headline'; import AppStore from './AppStore'; import RegisterToVote from './RegisterToVote'; +import { FundPhases } from '../../../stores/VotingStore'; +import type { FundPhase } from '../../../stores/VotingStore'; type Props = { currentLocale: Locale, currentDateFormat: string, currentTimeFormat: string, + fundPhase: FundPhase, onRegisterToVoteClick: Function, onExternalLinkClick: Function, }; -@observer -export default class VotingInfo extends Component { - static contextTypes = { - intl: intlShape.isRequired, - }; +const phaseToComponentMap = { + [FundPhases.SNAPSHOT]: SnapshotPhase, + [FundPhases.VOTING]: VotingPhase, + [FundPhases.TALLYING]: TallyingPhase, + [FundPhases.RESULTS]: ResultsPhase, +}; - render() { - const { - currentLocale, - currentDateFormat, - currentTimeFormat, - onRegisterToVoteClick, - onExternalLinkClick, - } = this.props; +const VotingInfo = ({ + currentLocale, + currentDateFormat, + currentTimeFormat, + fundPhase, + onRegisterToVoteClick, + onExternalLinkClick, +}: Props) => { + const PhaseComponent = phaseToComponentMap[fundPhase || FundPhases.SNAPSHOT]; - return ( -
- - -
-
-
- -
- -
-
-
- + + +
+
+
+ +
+
- -
- ); - } -} +
+ +
+
+ +
+ ); +}; + +export default observer(VotingInfo); diff --git a/source/renderer/app/components/voting/voting-info/VotingInfo.messages.js b/source/renderer/app/components/voting/voting-info/VotingInfo.messages.js new file mode 100644 index 0000000000..68d1f55e54 --- /dev/null +++ b/source/renderer/app/components/voting/voting-info/VotingInfo.messages.js @@ -0,0 +1,10 @@ +// @flow +import { defineMessages } from 'react-intl'; + +export const messages = defineMessages({ + fundName: { + id: 'voting.fundName', + defaultMessage: '!!!Fund{votingFundNumber}', + description: 'Current fund name', + }, +}); diff --git a/source/renderer/app/components/voting/voting-info/VotingInfo.scss b/source/renderer/app/components/voting/voting-info/VotingInfo.scss index ebe5293c68..72ddf75471 100644 --- a/source/renderer/app/components/voting/voting-info/VotingInfo.scss +++ b/source/renderer/app/components/voting/voting-info/VotingInfo.scss @@ -5,8 +5,10 @@ padding: 20px; .separator { + border: 1px solid var(--theme-voting-separator-color); + border-bottom-width: 0; margin: 20px 0; - opacity: 0.2; + width: 100%; } .bottomContent { diff --git a/source/renderer/app/components/voting/voting-info/VotingPhase.js b/source/renderer/app/components/voting/voting-info/VotingPhase.js new file mode 100644 index 0000000000..64f4d875a2 --- /dev/null +++ b/source/renderer/app/components/voting/voting-info/VotingPhase.js @@ -0,0 +1,75 @@ +// @flow +import React from 'react'; +import { observer } from 'mobx-react'; +import { injectIntl } from 'react-intl'; +import { + CURRENT_VOTING_FUND_NUMBER, + VOTING_CAST_START_DATE, + VOTING_CAST_END_DATE, +} from '../../../config/votingConfig'; +import { + formattedDateTime, + mapToLongDateTimeFormat, +} from '../../../utils/formatters'; +import type { Locale } from '../../../../../common/types/locales.types'; +import type { Intl } from '../../../types/i18nTypes'; +import { messages } from './VotingPhase.messages'; +import { messages as votingMessages } from './VotingInfo.messages'; +import styles from './CurrentPhase.scss'; +import votingStyles from './VotingInfo.scss'; + +type Props = { + currentLocale: Locale, + currentDateFormat: string, + currentTimeFormat: string, + intl: Intl, +}; + +function VotingPhase({ + currentLocale, + currentDateFormat, + currentTimeFormat, + intl, +}: Props) { + const mappedFormats = mapToLongDateTimeFormat({ + currentLocale, + currentDateFormat, + currentTimeFormat, + }); + + const startDate = formattedDateTime(VOTING_CAST_START_DATE, { + currentLocale, + currentDateFormat: mappedFormats.currentDateFormat, + }); + + const endDate = formattedDateTime(VOTING_CAST_END_DATE, { + currentLocale, + currentDateFormat: mappedFormats.currentDateFormat, + }); + + return ( +
+

+ {intl.formatMessage(votingMessages.fundName, { + votingFundNumber: CURRENT_VOTING_FUND_NUMBER, + })} +

+
+ + {intl.formatMessage(messages.dateLabel)} + + + {startDate} – {endDate} + +
+
+
+ + {intl.formatMessage(messages.instruction)} + +
+
+ ); +} + +export default injectIntl(observer(VotingPhase)); diff --git a/source/renderer/app/components/voting/voting-info/VotingPhase.messages.js b/source/renderer/app/components/voting/voting-info/VotingPhase.messages.js new file mode 100644 index 0000000000..49fe8c6fdd --- /dev/null +++ b/source/renderer/app/components/voting/voting-info/VotingPhase.messages.js @@ -0,0 +1,15 @@ +// @flow +import { defineMessages } from 'react-intl'; + +export const messages = defineMessages({ + dateLabel: { + id: 'voting.votingOpenPhase.dateLabel', + defaultMessage: '!!!Voting period open between:', + description: 'Voting date label', + }, + instruction: { + id: 'voting.votingOpenPhase.instruction', + defaultMessage: '!!!Use mobile app to vote', + description: 'Voting instruction', + }, +}); diff --git a/source/renderer/app/config/votingConfig.js b/source/renderer/app/config/votingConfig.js index 6dcc3203f4..ff3464abd0 100644 --- a/source/renderer/app/config/votingConfig.js +++ b/source/renderer/app/config/votingConfig.js @@ -1,23 +1,17 @@ // @flow const { isDev } = global.environment; -export const IS_VOTING_REGISTRATION_AVAILABLE = true; -export const CURRENT_VOTING_FUND_NUMBER = 6; +export const CURRENT_VOTING_FUND_NUMBER = 7; export const NEXT_VOTING_FUND_NUMBER = CURRENT_VOTING_FUND_NUMBER + 1; export const VOTING_REGISTRATION_MIN_WALLET_FUNDS = 500; // 500 ADA | unit: ADA export const VOTING_REGISTRATION_FEE_CALCULATION_AMOUNT = 1; // 1 ADA | unit: ADA export const VOTING_REGISTRATION_PIN_CODE_LENGTH = 4; export const VOTING_REGISTRATION_MIN_TRANSACTION_CONFIRMATIONS = isDev ? 2 : 10; export const VOTING_REGISTRATION_TRANSACTION_POLLING_INTERVAL = 1000; // 1 second | unit: milliseconds -export const VOTING_REGISTRATION_END_DATE = new Date('Jan 6, 2022, 11:00 UTC'); -export const VOTING_REGISTRATION_END_CHECK_INTERVAL = 3000; // 3 seconds | unit: milliseconds -export const VOTING_REGISTRATION_CAST_START_DATE = new Date( - 'Oct 7, 2021, 11:00 UTC' -); -export const VOTING_REGISTRATION_CAST_END_DATE = new Date( - 'Oct 21, 2021, 11:00 UTC' -); -export const VOTING_REGISTRATION_NEW_START_DATE = new Date( - 'Nov 11, 2021, 11:00 UTC' -); +export const VOTING_PHASE_CHECK_INTERVAL = 60000; // 60 seconds | unit: milliseconds +export const VOTING_SNAPSHOT_DATE = new Date('Jan 6, 2022, 11:00 UTC'); +export const VOTING_CAST_START_DATE = new Date('Jan 13, 2022, 11:00 UTC'); +export const VOTING_CAST_END_DATE = new Date('Jan 27, 2022, 11:00 UTC'); +export const VOTING_RESULTS_DATE = new Date('Feb 3, 2022'); +export const VOTING_NEW_SNAPSHOT_DATE = new Date('Apr 7, 2022, 11:00 UTC'); export const VOTING_REWARD = 1040000; // 1,040,000 USD | Unit: dollar diff --git a/source/renderer/app/containers/voting/VotingRegistrationPage.js b/source/renderer/app/containers/voting/VotingRegistrationPage.js index e8a6a31a18..bd4562137a 100644 --- a/source/renderer/app/containers/voting/VotingRegistrationPage.js +++ b/source/renderer/app/containers/voting/VotingRegistrationPage.js @@ -2,10 +2,7 @@ import React, { Component } from 'react'; import { observer, inject } from 'mobx-react'; import Layout from '../MainLayout'; -import { - IS_VOTING_REGISTRATION_AVAILABLE, - VOTING_REGISTRATION_MIN_WALLET_FUNDS, -} from '../../config/votingConfig'; +import { VOTING_REGISTRATION_MIN_WALLET_FUNDS } from '../../config/votingConfig'; import VerticalFlexContainer from '../../components/layout/VerticalFlexContainer'; import VotingInfo from '../../components/voting/voting-info/VotingInfo'; import VotingNoWallets from '../../components/voting/VotingNoWallets'; @@ -28,19 +25,14 @@ export default class VotingRegistrationPage extends Component { }; getInnerContent = (isVotingRegistrationDialogOpen: boolean) => { - const { app, networkStatus, wallets, voting, profile } = this.props.stores; + const { app, networkStatus, wallets, profile, voting } = this.props.stores; const { isSynced, syncPercentage } = networkStatus; - const { isRegistrationEnded } = voting; const { openExternalLink } = app; - if ( - !IS_VOTING_REGISTRATION_AVAILABLE || - (!isSynced && !isVotingRegistrationDialogOpen) - ) { + if (!isSynced && !isVotingRegistrationDialogOpen) { return ( ); @@ -58,10 +50,10 @@ export default class VotingRegistrationPage extends Component { const { currentTimeFormat, currentDateFormat, currentLocale } = profile; return ( this.props.actions.dialogs.open.trigger({ dialog: VotingRegistrationDialog, diff --git a/source/renderer/app/i18n/locales/defaultMessages.json b/source/renderer/app/i18n/locales/defaultMessages.json index e8030f5598..6dde82ee1d 100644 --- a/source/renderer/app/i18n/locales/defaultMessages.json +++ b/source/renderer/app/i18n/locales/defaultMessages.json @@ -7991,67 +7991,6 @@ ], "path": "source/renderer/app/components/voting/voting-info/AppStore.messages.json" }, - { - "descriptors": [ - { - "defaultMessage": "!!!Fund{currentVotingFundNumber}", - "description": "Current fund name", - "end": { - "column": 3, - "line": 9 - }, - "file": "source/renderer/app/components/voting/voting-info/CurrentFund.messages.js", - "id": "voting.currentFund.name", - "start": { - "column": 8, - "line": 5 - } - }, - { - "defaultMessage": "!!!End of voting:", - "description": "Headline for end date", - "end": { - "column": 3, - "line": 14 - }, - "file": "source/renderer/app/components/voting/voting-info/CurrentFund.messages.js", - "id": "voting.currentFund.headingForEndDate", - "start": { - "column": 21, - "line": 10 - } - }, - { - "defaultMessage": "!!!View results", - "description": "View resuls link label for Fund{currentVotingFundNumber}", - "end": { - "column": 3, - "line": 19 - }, - "file": "source/renderer/app/components/voting/voting-info/CurrentFund.messages.js", - "id": "voting.currentFund.viewResultsLinkLabel", - "start": { - "column": 24, - "line": 15 - } - }, - { - "defaultMessage": "https://cardano.ideascale.com/a/pages/results", - "description": "View results from Fund{currentVotingFundNumber}", - "end": { - "column": 3, - "line": 24 - }, - "file": "source/renderer/app/components/voting/voting-info/CurrentFund.messages.js", - "id": "voting.currentFund.viewResultsLinkURL", - "start": { - "column": 22, - "line": 20 - } - } - ], - "path": "source/renderer/app/components/voting/voting-info/CurrentFund.messages.json" - }, { "descriptors": [ { @@ -8129,32 +8068,18 @@ }, { "descriptors": [ - { - "defaultMessage": "!!!Fund{nextVotingFundNumber}", - "description": "Regiter to fund name", - "end": { - "column": 3, - "line": 9 - }, - "file": "source/renderer/app/components/voting/voting-info/RegisterToVote.messages.js", - "id": "voting.registerToVote.name", - "start": { - "column": 8, - "line": 5 - } - }, { "defaultMessage": "!!!Snapshot date:", "description": "Voting info snapshot date label", "end": { "column": 3, - "line": 14 + "line": 9 }, "file": "source/renderer/app/components/voting/voting-info/RegisterToVote.messages.js", "id": "voting.registerToVote.dateLabel", "start": { "column": 13, - "line": 10 + "line": 5 } }, { @@ -8162,13 +8087,13 @@ "description": "Steps to follow title", "end": { "column": 3, - "line": 19 + "line": 14 }, "file": "source/renderer/app/components/voting/voting-info/RegisterToVote.messages.js", "id": "voting.registerToVote.stepsTitle", "start": { "column": 14, - "line": 15 + "line": 10 } }, { @@ -8176,13 +8101,13 @@ "description": "First step to follow in order to vote", "end": { "column": 3, - "line": 24 + "line": 19 }, "file": "source/renderer/app/components/voting/voting-info/RegisterToVote.messages.js", "id": "voting.registerToVote.step1CheckBoxLabel", "start": { "column": 22, - "line": 20 + "line": 15 } }, { @@ -8190,13 +8115,13 @@ "description": "Second step to follow in order to vote", "end": { "column": 3, - "line": 30 + "line": 25 }, "file": "source/renderer/app/components/voting/voting-info/RegisterToVote.messages.js", "id": "voting.registerToVote.step2CheckBoxLabel", "start": { "column": 22, - "line": 25 + "line": 20 } }, { @@ -8204,18 +8129,183 @@ "description": "Button Label for voting registration steps", "end": { "column": 3, - "line": 35 + "line": 30 }, "file": "source/renderer/app/components/voting/voting-info/RegisterToVote.messages.js", "id": "voting.registerToVote.registerToVoteButtonLabel", "start": { "column": 15, - "line": 31 + "line": 26 } } ], "path": "source/renderer/app/components/voting/voting-info/RegisterToVote.messages.json" }, + { + "descriptors": [ + { + "defaultMessage": "!!!End of voting:", + "description": "Headline for end date", + "end": { + "column": 3, + "line": 9 + }, + "file": "source/renderer/app/components/voting/voting-info/ResultsPhase.messages.js", + "id": "voting.resultsPhase.endDateLabel", + "start": { + "column": 16, + "line": 5 + } + }, + { + "defaultMessage": "!!!View results", + "description": "View results link label", + "end": { + "column": 3, + "line": 14 + }, + "file": "source/renderer/app/components/voting/voting-info/ResultsPhase.messages.js", + "id": "voting.resultsPhase.viewResultsLinkLabel", + "start": { + "column": 24, + "line": 10 + } + }, + { + "defaultMessage": "https://cardano.ideascale.com/a/pages/results", + "description": "View results link", + "end": { + "column": 3, + "line": 19 + }, + "file": "source/renderer/app/components/voting/voting-info/ResultsPhase.messages.js", + "id": "voting.resultsPhase.viewResultsLinkURL", + "start": { + "column": 22, + "line": 15 + } + } + ], + "path": "source/renderer/app/components/voting/voting-info/ResultsPhase.messages.json" + }, + { + "descriptors": [ + { + "defaultMessage": "!!!Snapshot date:", + "description": "Snapshot date label", + "end": { + "column": 3, + "line": 9 + }, + "file": "source/renderer/app/components/voting/voting-info/SnapshotPhase.messages.js", + "id": "voting.snapshotPhase.snapshotDateLabel", + "start": { + "column": 21, + "line": 5 + } + }, + { + "defaultMessage": "!!!Next voting period:", + "description": "Next voting date label", + "end": { + "column": 3, + "line": 14 + }, + "file": "source/renderer/app/components/voting/voting-info/SnapshotPhase.messages.js", + "id": "voting.snapshotPhase.votingDateLabel", + "start": { + "column": 19, + "line": 10 + } + } + ], + "path": "source/renderer/app/components/voting/voting-info/SnapshotPhase.messages.json" + }, + { + "descriptors": [ + { + "defaultMessage": "!!!Voting ended:", + "description": "Voting end date label", + "end": { + "column": 3, + "line": 9 + }, + "file": "source/renderer/app/components/voting/voting-info/TallyingPhase.messages.js", + "id": "voting.tallyingPhase.endDateLabel", + "start": { + "column": 16, + "line": 5 + } + }, + { + "defaultMessage": "!!!Check back for results on:", + "description": "Results date label", + "end": { + "column": 3, + "line": 14 + }, + "file": "source/renderer/app/components/voting/voting-info/TallyingPhase.messages.js", + "id": "voting.tallyingPhase.resultsLabel", + "start": { + "column": 16, + "line": 10 + } + } + ], + "path": "source/renderer/app/components/voting/voting-info/TallyingPhase.messages.json" + }, + { + "descriptors": [ + { + "defaultMessage": "!!!Fund{votingFundNumber}", + "description": "Current fund name", + "end": { + "column": 3, + "line": 9 + }, + "file": "source/renderer/app/components/voting/voting-info/VotingInfo.messages.js", + "id": "voting.fundName", + "start": { + "column": 12, + "line": 5 + } + } + ], + "path": "source/renderer/app/components/voting/voting-info/VotingInfo.messages.json" + }, + { + "descriptors": [ + { + "defaultMessage": "!!!Voting period open between:", + "description": "Voting date label", + "end": { + "column": 3, + "line": 9 + }, + "file": "source/renderer/app/components/voting/voting-info/VotingPhase.messages.js", + "id": "voting.votingOpenPhase.dateLabel", + "start": { + "column": 13, + "line": 5 + } + }, + { + "defaultMessage": "!!!Use mobile app to vote", + "description": "Voting instruction", + "end": { + "column": 3, + "line": 14 + }, + "file": "source/renderer/app/components/voting/voting-info/VotingPhase.messages.js", + "id": "voting.votingOpenPhase.instruction", + "start": { + "column": 15, + "line": 10 + } + } + ], + "path": "source/renderer/app/components/voting/voting-info/VotingPhase.messages.json" + }, { "descriptors": [ { @@ -9003,109 +9093,6 @@ ], "path": "source/renderer/app/components/voting/VotingNoWallets.json" }, - { - "descriptors": [ - { - "defaultMessage": "!!!Project Catalyst voting registration", - "description": "Headline for the \"Voting unavailable\" screen", - "end": { - "column": 3, - "line": 25 - }, - "file": "source/renderer/app/components/voting/VotingUnavailable.js", - "id": "voting.unavailable.heading", - "start": { - "column": 11, - "line": 21 - } - }, - { - "defaultMessage": "!!!Project Catalyst Fund{currentVotingFundNumber} has now ended. Fund{nextVotingFundNumber} is currently in preparation, voting registration is not available yet.", - "description": "First paragraph on the \"Voting unavailable\" screen", - "end": { - "column": 3, - "line": 31 - }, - "file": "source/renderer/app/components/voting/VotingUnavailable.js", - "id": "voting.unavailable.paragraph1", - "start": { - "column": 14, - "line": 26 - } - }, - { - "defaultMessage": "!!!Join our {link1} and {link2} Telegram channels for the latest updates (English language only).", - "description": "Second paragraph on the \"Voting unavailable\" screen", - "end": { - "column": 3, - "line": 37 - }, - "file": "source/renderer/app/components/voting/VotingUnavailable.js", - "id": "voting.unavailable.paragraph2", - "start": { - "column": 14, - "line": 32 - } - }, - { - "defaultMessage": "!!!Catalyst Announcements", - "description": "First link text on the \"Voting unavailable\" screen", - "end": { - "column": 3, - "line": 42 - }, - "file": "source/renderer/app/components/voting/VotingUnavailable.js", - "id": "voting.unavailable.link1Text", - "start": { - "column": 13, - "line": 38 - } - }, - { - "defaultMessage": "!!!Project Catalyst Chat", - "description": "Second link text on the \"Voting unavailable\" screen", - "end": { - "column": 3, - "line": 47 - }, - "file": "source/renderer/app/components/voting/VotingUnavailable.js", - "id": "voting.unavailable.link2Text", - "start": { - "column": 13, - "line": 43 - } - }, - { - "defaultMessage": "!!!https://t.me/cardanocatalyst", - "description": "First link URL on the \"Voting unavailable\" screen", - "end": { - "column": 3, - "line": 52 - }, - "file": "source/renderer/app/components/voting/VotingUnavailable.js", - "id": "voting.unavailable.link1Url", - "start": { - "column": 12, - "line": 48 - } - }, - { - "defaultMessage": "!!!https://t.me/ProjectCatalystChat", - "description": "Second link URL on the \"Voting unavailable\" screen", - "end": { - "column": 3, - "line": 57 - }, - "file": "source/renderer/app/components/voting/VotingUnavailable.js", - "id": "voting.unavailable.link2Url", - "start": { - "column": 12, - "line": 53 - } - } - ], - "path": "source/renderer/app/components/voting/VotingUnavailable.json" - }, { "descriptors": [ { diff --git a/source/renderer/app/i18n/locales/en-US.json b/source/renderer/app/i18n/locales/en-US.json index bc2bec89a7..525b0e7ffd 100755 --- a/source/renderer/app/i18n/locales/en-US.json +++ b/source/renderer/app/i18n/locales/en-US.json @@ -637,10 +637,7 @@ "voting.catalystFooterLinks.community": "Community Chat", "voting.catalystFooterLinks.newsletter": "Newsletter", "voting.catalystFooterLinks.projects": "Projects", - "voting.currentFund.headingForEndDate": "End of voting:", - "voting.currentFund.name": "Fund{currentVotingFundNumber}", - "voting.currentFund.viewResultsLinkLabel": "View results", - "voting.currentFund.viewResultsLinkURL": "https://cardano.ideascale.com/a/pages/results", + "voting.fundName": "Fund{votingFundNumber}", "voting.info.androidAppButtonUrl": "https://play.google.com/store/apps/details?id=io.iohk.vitvoting", "voting.info.appleAppButtonUrl": "https://apps.apple.com/in/app/catalyst-voting/id1517473397", "voting.info.learnMoreLinkLabel": "Learn more", @@ -649,18 +646,19 @@ "voting.info.noWallets.headLine": "Voting registration for Fund{nextVotingFundNumber} is not available as you currently do not have any Shelley-compatible wallets.", "voting.info.noWallets.instructions": "Create a new wallet and transfer a minimum of {minVotingFunds} ADA (or restore an existing wallet with funds), then return here to register for voting.", "voting.registerToVote.dateLabel": "Snapshot date:", - "voting.registerToVote.name": "Fund{nextVotingFundNumber}", "voting.registerToVote.registerToVoteButtonLabel": "Register to vote", "voting.registerToVote.step1CheckBoxLabel": "Download the Catalyst Voting app on your smartphone", "voting.registerToVote.step2CheckBoxLabel": "Ensure that you register and hold the necessary 500 ADA at the time of the snapshot.", "voting.registerToVote.stepsTitle": "Follow these steps to vote:", - "voting.unavailable.heading": "Project Catalyst voting registration", - "voting.unavailable.link1Text": "Catalyst Announcements", - "voting.unavailable.link1Url": "https://t.me/cardanocatalyst", - "voting.unavailable.link2Text": "Project Catalyst Chat", - "voting.unavailable.link2Url": "https://t.me/ProjectCatalystChat", - "voting.unavailable.paragraph1": "Project Catalyst Fund{currentVotingFundNumber} has now ended. Fund{nextVotingFundNumber} is currently in preparation, voting registration is not available yet.", - "voting.unavailable.paragraph2": "Join our {link1} and {link2} Telegram channels for the latest updates (English language only).", + "voting.resultsPhase.endDateLabel": "End of voting:", + "voting.resultsPhase.viewResultsLinkLabel": "View results", + "voting.resultsPhase.viewResultsLinkURL": "https://cardano.ideascale.com/a/pages/results", + "voting.snapshotPhase.snapshotDateLabel": "Snapshot date:", + "voting.snapshotPhase.votingDateLabel": "Next voting period:", + "voting.tallyingPhase.endDateLabel": "Voting ended:", + "voting.tallyingPhase.resultsLabel": "Check back for results on:", + "voting.votingOpenPhase.dateLabel": "Voting period open between:", + "voting.votingOpenPhase.instruction": "Use mobile app to vote", "voting.votingRegistration.chooseWallet.step.continueButtonLabel": "Continue", "voting.votingRegistration.chooseWallet.step.description": "You can only use one wallet when registering. To maximize rewards and voting power, choose the wallet with the largest balance.", "voting.votingRegistration.chooseWallet.step.errorLegacyWallet": "This wallet cannot be registered for voting as it is a legacy Byron wallet.", diff --git a/source/renderer/app/i18n/locales/ja-JP.json b/source/renderer/app/i18n/locales/ja-JP.json index 0ce46dc7f1..5376f37759 100755 --- a/source/renderer/app/i18n/locales/ja-JP.json +++ b/source/renderer/app/i18n/locales/ja-JP.json @@ -637,10 +637,7 @@ "voting.catalystFooterLinks.community": "コミュニティチャット", "voting.catalystFooterLinks.newsletter": "ニュースレター", "voting.catalystFooterLinks.projects": "プロジェクト", - "voting.currentFund.headingForEndDate": "投票締め切り:", - "voting.currentFund.name": "Fund{currentVotingFundNumber}", - "voting.currentFund.viewResultsLinkLabel": "結果を見る", - "voting.currentFund.viewResultsLinkURL": "https://cardano.ideascale.com/a/pages/results", + "voting.fundName": "Fund{votingFundNumber}", "voting.info.androidAppButtonUrl": "https://play.google.com/store/apps/details?id=io.iohk.vitvoting", "voting.info.appleAppButtonUrl": "https://apps.apple.com/in/app/catalyst-voting/id1517473397", "voting.info.learnMoreLinkLabel": "もっと知る", @@ -649,18 +646,19 @@ "voting.info.noWallets.headLine": "現在Shelley対応のウォレットがないため、Fund{nextVotingFundNumber}の有権者登録はできません。", "voting.info.noWallets.instructions": "ウォレットを作成し、そこに{minVotingFunds} ADA以上を移してから(または既存の資金入りウォレットを復元してから)、改めてここで有権者登録を行ってください。", "voting.registerToVote.dateLabel": "スナップショット日:", - "voting.registerToVote.name": "Fund{nextVotingFundNumber}", "voting.registerToVote.registerToVoteButtonLabel": "有権者登録をする", "voting.registerToVote.step1CheckBoxLabel": "スマートフォンにCatalyst Votingアプリをダウンロードします", "voting.registerToVote.step2CheckBoxLabel": "スナップショット実施の時点で、登録を済ませ、500ADAを保有していてください", "voting.registerToVote.stepsTitle": "投票方法:", - "voting.unavailable.heading": "Project Catalyst有権者登録", - "voting.unavailable.link1Text": "Catalyst案内", - "voting.unavailable.link1Url": "https://t.me/cardanocatalyst", - "voting.unavailable.link2Text": "Project Catalyst Chat", - "voting.unavailable.link2Url": "https://t.me/ProjectCatalystChat", - "voting.unavailable.paragraph1": "Project Catalyst Fund{currentVotingFundNumber}は終了しました。現在Fund{nextVotingFundNumber}は準備中です。有権者登録の開始までしばらくお待ちください。", - "voting.unavailable.paragraph2": "!最新情報は、{link1}やTelegramの{link2}でチェックしてください(英語のみ)。", + "voting.resultsPhase.endDateLabel": "投票締め切り:", + "voting.resultsPhase.viewResultsLinkLabel": "結果を見る", + "voting.resultsPhase.viewResultsLinkURL": "https://cardano.ideascale.com/a/pages/results", + "voting.snapshotPhase.snapshotDateLabel": "スナップショット日:", + "voting.snapshotPhase.votingDateLabel": "次回投票期間:", + "voting.tallyingPhase.endDateLabel": "投票締め切り:", + "voting.tallyingPhase.resultsLabel": "結果発表:", + "voting.votingOpenPhase.dateLabel": "投票期間:", + "voting.votingOpenPhase.instruction": "モバイルアプリから投票してください", "voting.votingRegistration.chooseWallet.step.continueButtonLabel": "続ける", "voting.votingRegistration.chooseWallet.step.description": "投票に使用できるウォレットは1つだけです。報酬と議決権を最大にするには、残高の一番大きなウォレットを選択してください。", "voting.votingRegistration.chooseWallet.step.errorLegacyWallet": "このウォレットはByronレガシーウォレットであるため、有権者登録に使用できません。", diff --git a/source/renderer/app/stores/VotingStore.js b/source/renderer/app/stores/VotingStore.js index e4bc3bc0df..727b4ba978 100644 --- a/source/renderer/app/stores/VotingStore.js +++ b/source/renderer/app/stores/VotingStore.js @@ -11,11 +11,13 @@ import { import { formattedArrayBufferToHexString } from '../utils/formatters'; import walletUtils from '../utils/walletUtils'; import { + VOTING_CAST_START_DATE, + VOTING_CAST_END_DATE, + VOTING_RESULTS_DATE, + VOTING_PHASE_CHECK_INTERVAL, VOTING_REGISTRATION_TRANSACTION_POLLING_INTERVAL, VOTING_REGISTRATION_MIN_TRANSACTION_CONFIRMATIONS, NEXT_VOTING_FUND_NUMBER, - VOTING_REGISTRATION_END_DATE, - VOTING_REGISTRATION_END_CHECK_INTERVAL, } from '../config/votingConfig'; import { votingPDFGenerator } from '../utils/votingPDFGenerator'; import { i18nContext } from '../utils/i18nContext'; @@ -38,6 +40,14 @@ export type VotingDataType = { absoluteSlotNumber: number, }; +export type FundPhase = 'snapshot' | 'voting' | 'tallying' | 'results'; +export const FundPhases: EnumMap = { + SNAPSHOT: 'snapshot', + VOTING: 'voting', + TALLYING: 'tallying', + RESULTS: 'results', +}; + export default class VotingStore extends Store { @observable registrationStep: number = 1; @observable selectedWalletId: ?string = null; @@ -48,10 +58,10 @@ export default class VotingStore extends Store { @observable votingRegistrationKey: ?VotingRegistrationKeyType = null; @observable qrCode: ?string = null; @observable isConfirmationDialogOpen: boolean = false; - @observable isRegistrationEnded: boolean = false; + @observable fundPhase: FundPhase = FundPhases.SNAPSHOT; transactionPollingInterval: ?IntervalID = null; - registrationEndCheckInterval: ?IntervalID = null; + fundPhaseInterval: ?IntervalID = null; setup() { const { voting: votingActions } = this.actions; @@ -66,7 +76,7 @@ export default class VotingStore extends Store { votingActions.resetRegistration.listen(this._resetRegistration); votingActions.showConfirmationDialog.listen(this._showConfirmationDialog); votingActions.closeConfirmationDialog.listen(this._closeConfirmationDialog); - this._initializeRegistrationEndCheckInterval(); + this._initializeFundPhaseInterval(); } // REQUESTS @@ -125,10 +135,12 @@ export default class VotingStore extends Store { this.getWalletPublicKeyRequest.reset(); this.createVotingRegistrationTransactionRequest.reset(); this.signMetadataRequest.reset(); - if (this.transactionPollingInterval) + if (this.transactionPollingInterval) { clearInterval(this.transactionPollingInterval); - if (this.registrationEndCheckInterval) - clearInterval(this.registrationEndCheckInterval); + } + if (this.fundPhaseInterval) { + clearInterval(this.fundPhaseInterval); + } }; @action _startTransactionPolling = () => { @@ -139,12 +151,14 @@ export default class VotingStore extends Store { }, VOTING_REGISTRATION_TRANSACTION_POLLING_INTERVAL); }; - @action _initializeRegistrationEndCheckInterval = () => { - if (this.registrationEndCheckInterval) - clearInterval(this.registrationEndCheckInterval); - this.registrationEndCheckInterval = setInterval(() => { - this._checkVotingRegistrationEnd(); - }, VOTING_REGISTRATION_END_CHECK_INTERVAL); + @action _initializeFundPhaseInterval = () => { + if (this.fundPhaseInterval) { + clearInterval(this.fundPhaseInterval); + } + + this.fundPhaseInterval = setInterval(() => { + this._checkFundPhase(new Date()); + }, VOTING_PHASE_CHECK_INTERVAL); }; @action _setVotingRegistrationKey = (value: VotingRegistrationKeyType) => { @@ -433,8 +447,19 @@ export default class VotingStore extends Store { } }; - @action _checkVotingRegistrationEnd = () => { - this.isRegistrationEnded = new Date() >= VOTING_REGISTRATION_END_DATE; + @action _checkFundPhase = (now: Date) => { + const phaseValidation = { + [FundPhases.SNAPSHOT]: (date: Date) => date < VOTING_CAST_START_DATE, + [FundPhases.VOTING]: (date: Date) => + now >= VOTING_CAST_START_DATE && date < VOTING_CAST_END_DATE, + [FundPhases.TALLYING]: (date: Date) => + now >= VOTING_CAST_END_DATE && date < VOTING_RESULTS_DATE, + [FundPhases.RESULTS]: (date: Date) => date >= VOTING_RESULTS_DATE, + }; + this.fundPhase = + Object.keys(FundPhases) + .map((key) => FundPhases[key]) + .find((phase) => phaseValidation[phase](now)) || FundPhases.SNAPSHOT; }; _generateVotingRegistrationKey = async () => { diff --git a/source/renderer/app/stores/VotingStore.spec.js b/source/renderer/app/stores/VotingStore.spec.js new file mode 100644 index 0000000000..b97ad1a085 --- /dev/null +++ b/source/renderer/app/stores/VotingStore.spec.js @@ -0,0 +1,40 @@ +// @flow +import type { Api } from '../api/index'; +import type { ActionsMap } from '../actions/index'; +import VotingStore, { FundPhases } from './VotingStore'; +import { + VOTING_CAST_END_DATE, + VOTING_CAST_START_DATE, + VOTING_RESULTS_DATE, + VOTING_SNAPSHOT_DATE, +} from '../config/votingConfig'; + +describe('VotingStore', () => { + const api: Api = ({ + ada: jest.fn(), + }: any); + + const actions: ActionsMap = (jest.fn(): any); + + const cases = [ + [new Date(VOTING_SNAPSHOT_DATE.getTime() - 60000), FundPhases.SNAPSHOT], + [VOTING_SNAPSHOT_DATE, FundPhases.SNAPSHOT], + [new Date(VOTING_CAST_START_DATE.getTime() - 60000), FundPhases.SNAPSHOT], + [VOTING_CAST_START_DATE, FundPhases.VOTING], + [new Date(VOTING_CAST_END_DATE.getTime() - 60000), FundPhases.VOTING], + [VOTING_CAST_END_DATE, FundPhases.TALLYING], + [new Date(VOTING_RESULTS_DATE.getTime() - 60000), FundPhases.TALLYING], + [VOTING_RESULTS_DATE, FundPhases.RESULTS], + ]; + + test.each(cases)( + `should have correct fund phase for date %s - %s phase`, + (date, expected) => { + const votingStore = new VotingStore(api, actions); + + votingStore._checkFundPhase(date); + + expect(votingStore.fundPhase).toEqual(expected); + } + ); +}); diff --git a/source/renderer/app/utils/formatters.js b/source/renderer/app/utils/formatters.js index 2a422a748e..31bea3cb4c 100644 --- a/source/renderer/app/utils/formatters.js +++ b/source/renderer/app/utils/formatters.js @@ -287,26 +287,29 @@ export const formattedSize = (size: string): string => { return formattedResult; }; +type CurrentFormats = { + currentLocale: Locale, + currentDateFormat: string, + currentTimeFormat?: string, +}; + export const formattedDateTime = ( dateTime: Date, - { - currentLocale, - currentDateFormat, - currentTimeFormat, - }: { - currentLocale: Locale, - currentDateFormat: string, - currentTimeFormat: string, - } + { currentLocale, currentDateFormat, currentTimeFormat }: CurrentFormats ) => { moment.locale(momentLocales[currentLocale]); const dateTimeMoment = moment(dateTime); const dateFormatted = dateTimeMoment.format(currentDateFormat); - const timeFormatted = dateTimeMoment.format(currentTimeFormat); - const dateTimeSeparator = DATE_TIME_SEPARATOR_MAP[currentDateFormat]; - return `${dateFormatted}${dateTimeSeparator}${timeFormatted}`; + if (currentTimeFormat) { + const timeFormatted = dateTimeMoment.format(currentTimeFormat); + const dateTimeSeparator = DATE_TIME_SEPARATOR_MAP[currentDateFormat]; + + return `${dateFormatted}${dateTimeSeparator}${timeFormatted}`; + } + + return dateFormatted; }; export const getMultiplierFromDecimalPlaces = (decimalPlaces: number) => @@ -316,11 +319,7 @@ export const mapToLongDateTimeFormat = ({ currentLocale, currentDateFormat, currentTimeFormat, -}: { - currentLocale: Locale, - currentDateFormat: string, - currentTimeFormat: string, -}) => { +}: CurrentFormats) => { const mappedDateFormat = currentLocale === LOCALES.english ? DATE_ENGLISH_LL_MAP_OPTIONS[currentDateFormat] diff --git a/storybook/stories/voting/Voting.stories.js b/storybook/stories/voting/Voting.stories.js index f01d3238ae..2618a07531 100644 --- a/storybook/stories/voting/Voting.stories.js +++ b/storybook/stories/voting/Voting.stories.js @@ -2,7 +2,7 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; -import { withKnobs, boolean, number } from '@storybook/addon-knobs'; +import { withKnobs, boolean, number, select } from '@storybook/addon-knobs'; import BigNumber from 'bignumber.js'; import StoryDecorator from '../_support/StoryDecorator'; import StoryProvider from '../_support/StoryProvider'; @@ -12,6 +12,7 @@ import VotingRegistrationStepsConfirm from '../../../source/renderer/app/compone import VotingRegistrationStepsEnterPinCode from '../../../source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsEnterPinCode'; import VotingRegistrationStepsQrCode from '../../../source/renderer/app/components/voting/voting-registration-wizard-steps/VotingRegistrationStepsQrCode'; import VotingInfo from '../../../source/renderer/app/components/voting/voting-info/VotingInfo'; +import { FundPhases } from '../../../source/renderer/app/stores/VotingStore'; import { VotingFooterLinks } from '../../../source/renderer/app/components/voting/VotingFooterLinks'; import { LANGUAGE_OPTIONS, @@ -174,10 +175,15 @@ storiesOf('Voting|Voting Info', module) .add('Voting Info', () => (