diff --git a/CHANGELOG.md b/CHANGELOG.md index 36278b6af6..71b7bfdb2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- Show "To be defined" if date of the next Catalyst fund is in the past ([PR 3105](https://github.com/input-output-hk/daedalus/pull/3105)) - Switched to the new Catalyst API ([PR 3129](https://github.com/input-output-hk/daedalus/pull/3129)) ### Fixes diff --git a/source/common/types/environment.types.ts b/source/common/types/environment.types.ts index 06fabda70e..2d41254765 100644 --- a/source/common/types/environment.types.ts +++ b/source/common/types/environment.types.ts @@ -35,6 +35,8 @@ export type Environment = { isBlankScreenFixActive: boolean; keepLocalClusterRunning: boolean; analyticsFeatureEnabled: boolean; + catalystApiUrlOverride?: string; + votingVisibleOverride: boolean; }; // constants export const PRODUCTION = 'production'; diff --git a/source/main/environment.ts b/source/main/environment.ts index 6f33b4c28e..17861a9680 100644 --- a/source/main/environment.ts +++ b/source/main/environment.ts @@ -112,6 +112,8 @@ export const environment: Environment = Object.assign( keepLocalClusterRunning, hasMetHardwareRequirements, analyticsFeatureEnabled, + catalystApiUrlOverride: process.env.CATALYST_API_URL_OVERRIDE, + votingVisibleOverride: process.env.VOTING_VISIBLE_OVERRIDE === 'true', }, process.env ); diff --git a/source/renderer/app/api/voting/requests/getCatalystFund.ts b/source/renderer/app/api/voting/requests/getCatalystFund.ts index 7d25602653..d215eef19b 100644 --- a/source/renderer/app/api/voting/requests/getCatalystFund.ts +++ b/source/renderer/app/api/voting/requests/getCatalystFund.ts @@ -2,10 +2,16 @@ import { externalRequest } from '../../utils/externalRequest'; import { CATALYST_API_URL } from '../../../config/urlsConfig'; import { GetCatalystFundResponse } from '../types'; -export const getCatalystFund = (): Promise => - externalRequest({ - hostname: CATALYST_API_URL, +export const getCatalystFund = (): Promise => { + const urlOverride: URL | undefined = environment.catalystApiUrlOverride + ? new URL(environment.catalystApiUrlOverride) + : undefined; + + return externalRequest({ + hostname: urlOverride ? urlOverride.hostname : CATALYST_API_URL, path: '/api/v0/fund', method: 'GET', - protocol: 'https', + port: urlOverride ? Number(urlOverride.port) : undefined, + protocol: urlOverride ? urlOverride.protocol.replace(':', '') : 'https', }); +}; diff --git a/source/renderer/app/components/voting/voting-info/RegisterToVote.messages.ts b/source/renderer/app/components/voting/voting-info/RegisterToVote.messages.ts index d275e36787..378731f850 100644 --- a/source/renderer/app/components/voting/voting-info/RegisterToVote.messages.ts +++ b/source/renderer/app/components/voting/voting-info/RegisterToVote.messages.ts @@ -27,4 +27,10 @@ export const messages = defineMessages({ defaultMessage: '!!!Register to vote', description: 'Button Label for voting registration steps', }, + toBeDefined: { + id: 'voting.resultsPhase.toBeDefined', + defaultMessage: '!!!To be defined', + description: + 'Text to show when Catalyst API is returning a past date value', + }, }); diff --git a/source/renderer/app/components/voting/voting-info/RegisterToVote.tsx b/source/renderer/app/components/voting/voting-info/RegisterToVote.tsx index 1c55f62f5c..bbcbe21f8b 100644 --- a/source/renderer/app/components/voting/voting-info/RegisterToVote.tsx +++ b/source/renderer/app/components/voting/voting-info/RegisterToVote.tsx @@ -1,5 +1,6 @@ import React, { useState } from 'react'; import { injectIntl } from 'react-intl'; +import moment from 'moment'; import { Button } from 'react-polymorph/lib/components/Button'; import { Checkbox } from 'react-polymorph/lib/components/Checkbox'; import { @@ -13,6 +14,7 @@ import { messages as votingMessages } from './VotingInfo.messages'; import styles from './RegisterToVote.scss'; import votingStyles from './VotingInfo.scss'; import type { CatalystFund } from '../../../api/voting/types'; +import { logger } from '../../../utils/logging'; type Props = { currentLocale: Locale; @@ -23,6 +25,17 @@ type Props = { onRegisterToVoteClick: (...args: Array) => any; }; +const isFutureDate = (date: Date): boolean => { + try { + return moment().diff(date) < 0; + } catch (error) { + logger.error('Voting::NextFund::Invalid date', { + error, + }); + } + return false; +}; + function RegisterToVote({ currentLocale, currentDateFormat, @@ -34,17 +47,19 @@ function RegisterToVote({ const [step1, setStep1] = useState(false); const [step2, setStep2] = useState(false); const canRegister = step1 && step2; - const snapshotDate = formattedDateTime( - fundInfo.next.registrationSnapshotTime, - { - currentLocale, - ...mapToLongDateTimeFormat({ + const nextSnapshotDateTime = fundInfo.next.registrationSnapshotTime; + + const snapshotDate = isFutureDate(nextSnapshotDateTime) + ? formattedDateTime(nextSnapshotDateTime, { currentLocale, - currentDateFormat, - currentTimeFormat, - }), - } - ); + ...mapToLongDateTimeFormat({ + currentLocale, + currentDateFormat, + currentTimeFormat, + }), + }) + : intl.formatMessage(messages.toBeDefined); + return (
diff --git a/source/renderer/app/config/urlsConfig.ts b/source/renderer/app/config/urlsConfig.ts index cb4be7a34a..b43740cecd 100644 --- a/source/renderer/app/config/urlsConfig.ts +++ b/source/renderer/app/config/urlsConfig.ts @@ -25,5 +25,7 @@ export const ALLOWED_EXTERNAL_HOSTNAMES = [ TESTNET_NEWS_HASH_URL, STAGING_NEWS_HASH_URL, coingeckoConfig.hostname, - CATALYST_API_URL, + environment.catalystApiUrlOverride + ? new URL(environment.catalystApiUrlOverride).hostname + : CATALYST_API_URL, ]; diff --git a/source/renderer/app/containers/voting/VotingRegistrationPage.tsx b/source/renderer/app/containers/voting/VotingRegistrationPage.tsx index cdd9997235..e0a88c9c0e 100644 --- a/source/renderer/app/containers/voting/VotingRegistrationPage.tsx +++ b/source/renderer/app/containers/voting/VotingRegistrationPage.tsx @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import { observer, inject } from 'mobx-react'; +import { inject, observer } from 'mobx-react'; import Layout from '../MainLayout'; import { VOTING_REGISTRATION_MIN_WALLET_FUNDS } from '../../config/votingConfig'; import VerticalFlexContainer from '../../components/layout/VerticalFlexContainer'; diff --git a/source/renderer/app/i18n/locales/defaultMessages.json b/source/renderer/app/i18n/locales/defaultMessages.json index b9230bc010..561c19c0fb 100644 --- a/source/renderer/app/i18n/locales/defaultMessages.json +++ b/source/renderer/app/i18n/locales/defaultMessages.json @@ -4475,6 +4475,11 @@ "defaultMessage": "!!!Register to vote", "description": "Button Label for voting registration steps", "id": "voting.registerToVote.registerToVoteButtonLabel" + }, + { + "defaultMessage": "!!!To be defined", + "description": "Text to show when Catalyst API is returning a past date value", + "id": "voting.resultsPhase.toBeDefined" } ], "path": "source/renderer/app/components/voting/voting-info/RegisterToVote.messages.ts" diff --git a/source/renderer/app/i18n/locales/en-US.json b/source/renderer/app/i18n/locales/en-US.json index 234577789d..989ad0cc4a 100755 --- a/source/renderer/app/i18n/locales/en-US.json +++ b/source/renderer/app/i18n/locales/en-US.json @@ -731,6 +731,7 @@ "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.resultsPhase.endDateLabel": "End of voting:", + "voting.resultsPhase.toBeDefined": "To be defined", "voting.resultsPhase.viewResultsLinkLabel": "View results", "voting.resultsPhase.viewResultsLinkURL": "https://cardano.ideascale.com/a/pages/results", "voting.snapshotPhase.snapshotDateLabel": "Snapshot date:", diff --git a/source/renderer/app/i18n/locales/ja-JP.json b/source/renderer/app/i18n/locales/ja-JP.json index 5ad29737f0..5014ea1fd7 100755 --- a/source/renderer/app/i18n/locales/ja-JP.json +++ b/source/renderer/app/i18n/locales/ja-JP.json @@ -731,6 +731,7 @@ "voting.registerToVote.step2CheckBoxLabel": "スナップショット実施の時点で、登録を済ませ、500ADAを保有していてください", "voting.registerToVote.stepsTitle": "投票方法:", "voting.resultsPhase.endDateLabel": "投票締め切り:", + "voting.resultsPhase.toBeDefined": "未定", "voting.resultsPhase.viewResultsLinkLabel": "結果を見る", "voting.resultsPhase.viewResultsLinkURL": "https://cardano.ideascale.com/a/pages/results", "voting.snapshotPhase.snapshotDateLabel": "スナップショット日:", diff --git a/source/renderer/app/stores/SidebarStore.ts b/source/renderer/app/stores/SidebarStore.ts index 1310ca29c2..c641e58690 100644 --- a/source/renderer/app/stores/SidebarStore.ts +++ b/source/renderer/app/stores/SidebarStore.ts @@ -130,7 +130,8 @@ export default class SidebarStore extends Store { [categories.STAKING_DELEGATION_COUNTDOWN.name]: false, [categories.STAKING.name]: true, [categories.SETTINGS.name]: true, - [categories.VOTING.name]: isMainnet || isDev, + [categories.VOTING.name]: + isMainnet || isDev || environment.votingVisibleOverride, [categories.NETWORK_INFO.name]: isFlight, }; const categoriesFilteredList: Array = list.filter( diff --git a/source/renderer/app/utils/PersistentTimeMachine.ts b/source/renderer/app/utils/PersistentTimeMachine.ts new file mode 100644 index 0000000000..88ce05e2d7 --- /dev/null +++ b/source/renderer/app/utils/PersistentTimeMachine.ts @@ -0,0 +1,42 @@ +import timeMachine from 'timemachine'; + +// https://github.com/schickling/timemachine/issues/8 +timeMachine.reset(); + +const TIME_MACHINE_SESSION_STORAGE_KEY = 'time_machine_date'; + +export class PersistentTimeMachine { + private _isInitialized = false; + + init() { + const dateString = sessionStorage.getItem(TIME_MACHINE_SESSION_STORAGE_KEY); + if (dateString !== null) { + timeMachine.config({ + dateString, + }); + } + this._isInitialized = true; + } + + enable(dateString: string) { + this._ensureIsInitialized(); + timeMachine.config({ + dateString, + }); + sessionStorage.setItem(TIME_MACHINE_SESSION_STORAGE_KEY, dateString); + window.location.reload(); + } + + disable() { + this._ensureIsInitialized(); + timeMachine.reset(); + sessionStorage.removeItem(TIME_MACHINE_SESSION_STORAGE_KEY); + window.location.reload(); + } + + private _ensureIsInitialized() { + if (!this._isInitialized) { + throw new Error('Time machine must be initialized before use'); + } + } +} diff --git a/source/renderer/app/utils/index.ts b/source/renderer/app/utils/index.ts index 5e54553940..de46e40991 100644 --- a/source/renderer/app/utils/index.ts +++ b/source/renderer/app/utils/index.ts @@ -1,7 +1,12 @@ import { generateMnemonic } from './crypto'; +import { PersistentTimeMachine } from './PersistentTimeMachine'; + +const timeMachine = new PersistentTimeMachine(); +timeMachine.init(); export default { crypto: { generateMnemonic, }, + timeMachine, }; diff --git a/translations/messages.json b/translations/messages.json index 1a3f80e1b7..d37ba3c962 100644 --- a/translations/messages.json +++ b/translations/messages.json @@ -4475,6 +4475,11 @@ "defaultMessage": "!!!Register to vote", "description": "Button Label for voting registration steps", "id": "voting.registerToVote.registerToVoteButtonLabel" + }, + { + "defaultMessage": "!!!To be defined", + "description": "Text to show when Catalyst API is returning a past date value", + "id": "voting.resultsPhase.toBeDefined" } ], "path": "source/renderer/app/components/voting/voting-info/RegisterToVote.messages.ts"