From debaaaae194bb5216cced1e2523e6327c04072f1 Mon Sep 17 00:00:00 2001 From: Mohan Date: Tue, 1 Oct 2024 18:59:59 +0530 Subject: [PATCH 1/7] feat: Implement UX improvements on rewards (#372) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: tray icon click, refactor for better DX around tray * fix: further nullish fallbacks * release: rc136 * fix: allowlist evm public keys in gitleaks CI * fix: updated for correct version * fix: edit [allowlist] regexes * fix: seperate allowlist for ignore evm pub keys * fix: make global allowlist array * fix: make allowlists singular * fix: combine allowlists * fix: use word boundaries * feat: refactor pearl tray * chore: delete old tray icons file * fix: use enum * bump: rc140 * fix: missing resize * chore: add electron.logger * fix: setTemplateImage for macOS tray icon * docs: updated docs * fix: missing setTemplateImage * fix: links * fix: only bind click events for windows (linux/mac do not support) * bump: rc142 * feat: Implement UX improvements on balances (#362) * feat: add constant for modal width * feat: add CustomModal component * feat: update modal width to use constant * feat: add AccountBalanceDetails dummy component * feat: add dummy OlasBalance in AccountBalanceDetails component * feat: add dummy XdaoBalance in AccountBalanceDetails component * Add YourWallet and Signer component * Add YourAgentWallet component * feat: add a button to trigger the modal * feat: remove comments code for balance * feat: integrate wallet address * feat: add signer, balances and update InfoBreakdown component * feat: extract Signer header * feat: add agent details * feat: as per new design * feat: update images * feat: Remove current balance tooltip * feat: renames * feat: replace icon with 'See breakdown' text * feat: Update account balance to include EOA xDAI for AccountBalances.tsx and YourAgent.tsx * feat: Compliance review comments (#366) * feat: update backup wallet message for clarity * remove: do not add more funds line * fix: 'This enables you to' -> 'you may recover your funds to your backup wallet if you lose ....' * feat: 'may' not 'can' * feat: 'Note that the backup wallet feature is not designed to restore access to your Pearl account but rather the funds ...' * feat: 'to avoid missing targets' * feat: Change to 'Your agent is at risk of missing its targets, which would result in several days' suspension.' * feat: Change to 'to be eligible for...' and Add ',' * feat: Change to 'Estimated Annual Percentage Yield (APY)'. Implement it across' * feat: Remove 'will' https * fix: broken .nvmrc * fix: stops build warning * fix: removed macUpdater listener, keeps firing after changing options * refactor: moved macUpdater event from main (clutter) * fix: use app.getAppPath, __dirname doesn't work in subdirectories * fix: return * feat: display last transaction with a 1-minute delay – persists even after navigating to other pages (#365) * feat: add a hook to delay 1 minute to show last transaction * changes * feat: update logic * release: rc143 * fix: missing `?` * feat: add win32 dev build * feat: UI of nested wallets in new Wallet screen (#368) * feat: update 'Address' component * feat: directory rename * feat: remove modal and create a different page for 'Your wallet' * feat: restructure Component order * fix: update tray icons & add retina images * feat: Show scrollbar when the content is taller that then window (#369) * feat: try with account-balances modal * feat: add scrollbar css globally * add scroll to botton on 'Add funds' click & add global scroll * feat: minor scroll-bar update * refactor: Add delay utility function for code readability * feat: hide body scroll and enable for main card * feat: add scroll to body * feat: fixed topbar * feat: init rewards history * feat: add design changes and mock * feat: fix style component warning and add queryClient * feat: integrate graphQL query to fetch rewards history * feat: add actual query (used in studio) * feat: remove comment * feat: update naming * feat: use epochLength * feat: update color * feat: add zod * feat: package change * feat: move date utils to time folder * feat: address Tanya review changes * feat: Add @tanstack/react-query and zod packages in electron package.json * feat: rewards history logic (#378) * feat: if there are no rewards, do not show the contract info altogether * chore: lots of tries * feat: hardcode the timestamp and ignore previous ones * feat: remove own comment for understading * feat: fetch service staked info * feat: add logic to get the staked time * feat: trying with serviceInfo.tsStart but no success * feat: use epoch 0 timestamp logic * feat: add comments --------- Co-authored-by: truemiller Co-authored-by: Josh Miller <31908788+truemiller@users.noreply.github.com> --- electron/install.js | 38 ++-- .../MainPage/sections/RewardsSection.tsx | 15 +- .../RewardsHistory/RewardsHistory.tsx | 206 ++++++++++++++++++ frontend/components/RewardsHistory/types.ts | 20 ++ .../RewardsHistory/useRewardsHistory.ts | 179 +++++++++++++++ frontend/components/styled/CardFlex.tsx | 10 +- frontend/constants/stakingProgramMeta.ts | 1 + .../context/StakingContractInfoProvider.tsx | 1 - frontend/enums/PageState.ts | 1 + frontend/package.json | 6 +- frontend/pages/_app.tsx | 11 +- frontend/pages/index.tsx | 3 + frontend/styles/globals.scss | 17 ++ frontend/theme/index.ts | 4 + frontend/types/Address.ts | 6 + frontend/utils/time.ts | 29 +++ frontend/yarn.lock | 100 ++++++--- package.json | 6 +- yarn.lock | 98 ++++++--- 19 files changed, 664 insertions(+), 87 deletions(-) create mode 100644 frontend/components/RewardsHistory/RewardsHistory.tsx create mode 100644 frontend/components/RewardsHistory/types.ts create mode 100644 frontend/components/RewardsHistory/useRewardsHistory.ts diff --git a/electron/install.js b/electron/install.js index 8b2eb64c..40682a49 100644 --- a/electron/install.js +++ b/electron/install.js @@ -6,7 +6,7 @@ const process = require('process'); const axios = require('axios'); const { spawnSync } = require('child_process'); const { logger } = require('./logger'); -const { execSync} = require('child_process'); +const { execSync } = require('child_process'); const { paths } = require('./constants'); const homedir = os.homedir(); /** @@ -55,18 +55,16 @@ const TendermintUrls = { }, }; - function execSyncExitCode(cmd) { try { execSync(cmd); return 0; - } - catch (error) { - logger.electron(error.status); // Might be 127 in your example. + } catch (error) { + logger.electron(error.status); // Might be 127 in your example. logger.electron(error.message); // Holds the message you typically want. - logger.electron(error.stderr.toString()); // Holds the stderr output. Use `.toString()`. - logger.electron(error.stdout.toString()); // Holds the stdout output. Use `.toString()`. - return error.status; + logger.electron(error.stderr.toString()); // Holds the stderr output. Use `.toString()`. + logger.electron(error.stdout.toString()); // Holds the stdout output. Use `.toString()`. + return error.status; } } @@ -94,7 +92,6 @@ function runCmdUnix(command, options) { logger.electron(`===== stderr ===== \n${output.stderr}`); } - function runSudoUnix(command, options) { let bin = getBinPath(command); if (!bin) { @@ -132,7 +129,7 @@ function isTendermintInstalledUnix() { function isTendermintInstalledWindows() { return true; //always installed cause bundled in - return execSyncExitCode('tendermint --help') === 0; + return execSyncExitCode('tendermint --help') === 0; } async function downloadFile(url, dest) { @@ -171,24 +168,23 @@ async function installTendermintWindows() { logger.electron(`Installing tendermint binary`); try { execSync('tar -xvf tendermint.tar.gz'); - } catch (error){ - logger.electron(error.status); // Might be 127 in your example. + } catch (error) { + logger.electron(error.status); // Might be 127 in your example. logger.electron(error.message); // Holds the message you typically want. - logger.electron(error.stderr.toString()); // Holds the stderr output. Use `.toString()`. - logger.electron(error.stdout.toString()); // Holds the stdout output. Use `.toString()`. + logger.electron(error.stderr.toString()); // Holds the stderr output. Use `.toString()`. + logger.electron(error.stdout.toString()); // Holds the stdout output. Use `.toString()`. } - const bin_dir = homedir + "//AppData//Local//Microsoft//WindowsApps//" + const bin_dir = homedir + '//AppData//Local//Microsoft//WindowsApps//'; if (!Env.CI) { if (!fs.existsSync(bin_dir)) { - fs.mkdirSync(bin_dir, {recursive: true}); + fs.mkdirSync(bin_dir, { recursive: true }); } - fs.copyFileSync("tendermint.exe", bin_dir + "tendermint.exe"); + fs.copyFileSync('tendermint.exe', bin_dir + 'tendermint.exe'); } process.chdir(cwd); } - async function installTendermintUnix() { logger.electron(`Installing tendermint for ${os.platform()}-${process.arch}`); const cwd = process.cwd(); @@ -259,14 +255,14 @@ async function setupUbuntu(ipcChannel) { } } - - async function setupWindows(ipcChannel) { logger.electron('Creating required directories'); await createDirectory(`${paths.dotOperateDirectory}`); await createDirectory(`${paths.tempDir}`); - logger.electron('Checking tendermint installation: ' + isTendermintInstalledWindows()); + logger.electron( + 'Checking tendermint installation: ' + isTendermintInstalledWindows(), + ); if (!isTendermintInstalledWindows()) { ipcChannel.send('response', 'Installing tendermint'); logger.electron('Installing tendermint'); diff --git a/frontend/components/MainPage/sections/RewardsSection.tsx b/frontend/components/MainPage/sections/RewardsSection.tsx index 7ca0e49c..3376812e 100644 --- a/frontend/components/MainPage/sections/RewardsSection.tsx +++ b/frontend/components/MainPage/sections/RewardsSection.tsx @@ -1,10 +1,12 @@ -import { InfoCircleOutlined } from '@ant-design/icons'; +import { InfoCircleOutlined, RightOutlined } from '@ant-design/icons'; import { Button, Flex, Modal, Skeleton, Tag, Tooltip, Typography } from 'antd'; import Image from 'next/image'; import { useCallback, useEffect, useRef, useState } from 'react'; +import { Pages } from '@/enums/PageState'; import { useBalance } from '@/hooks/useBalance'; import { useElectronApi } from '@/hooks/useElectronApi'; +import { usePageState } from '@/hooks/usePageState'; import { useReward } from '@/hooks/useReward'; import { useStore } from '@/hooks/useStore'; import { balanceFormat } from '@/utils/numberFormatters'; @@ -27,6 +29,7 @@ const getFormattedReward = (reward: number | undefined) => const DisplayRewards = () => { const { availableRewardsForEpochEth, isEligibleForRewards } = useReward(); const { isBalanceLoaded } = useBalance(); + const { goto } = usePageState(); const reward = getFormattedReward(availableRewardsForEpochEth); @@ -46,6 +49,7 @@ const DisplayRewards = () => { + {isBalanceLoaded ? ( {reward} OLAS  @@ -58,6 +62,15 @@ const DisplayRewards = () => { ) : ( )} + + goto(Pages.RewardsHistory)} + > + See rewards history + + ); }; diff --git a/frontend/components/RewardsHistory/RewardsHistory.tsx b/frontend/components/RewardsHistory/RewardsHistory.tsx new file mode 100644 index 00000000..2276785e --- /dev/null +++ b/frontend/components/RewardsHistory/RewardsHistory.tsx @@ -0,0 +1,206 @@ +import { + ApiOutlined, + CloseOutlined, + HistoryOutlined, + InfoCircleOutlined, +} from '@ant-design/icons'; +import { + Button, + Col, + ConfigProvider, + Flex, + Popover, + Row, + Spin, + Tag, + ThemeConfig, + Typography, +} from 'antd'; +import { CSSProperties, ReactNode, useMemo } from 'react'; +import styled from 'styled-components'; + +import { CardTitle } from '@/components/Card/CardTitle'; +import { CardFlex } from '@/components/styled/CardFlex'; +import { COLOR } from '@/constants/colors'; +import { UNICODE_SYMBOLS } from '@/constants/symbols'; +import { Pages } from '@/enums/PageState'; +import { usePageState } from '@/hooks/usePageState'; +import { balanceFormat } from '@/utils/numberFormatters'; +import { formatToMonthDay, formatToShortDateTime } from '@/utils/time'; + +import { EpochDetails, StakingReward } from './types'; +import { useRewardsHistory } from './useRewardsHistory'; + +const { Text, Title } = Typography; +const MIN_HEIGHT = 400; +const iconStyle: CSSProperties = { fontSize: 48, color: COLOR.TEXT_LIGHT }; + +const yourWalletTheme: ThemeConfig = { + components: { + Card: { paddingLG: 16 }, + }, +}; + +const ContractName = styled.div` + padding: 24px 24px 16px 24px; + border-bottom: 1px solid ${COLOR.BORDER_GRAY}; +`; + +const EpochRow = styled(Row)` + padding: 16px 24px; + border-bottom: 1px solid ${COLOR.BORDER_GRAY}; +`; + +const EarnedTag = () => ( + + Earned + +); + +const NotEarnedTag = () => ( + + Not earned + +); + +const Container = ({ children }: { children: ReactNode }) => ( + + {children} + +); + +const Loading = () => ( + + + +); + +const NoRewardsHistory = () => ( + + + There’s no history of rewards yet + +); + +const ErrorLoadingHistory = ({ refetch }: { refetch: () => void }) => ( + + + Error loading data + + +); + +const EpochTime = ({ epoch }: { epoch: EpochDetails }) => { + const timePeriod = useMemo(() => { + if (epoch.epochStartTimeStamp && epoch.epochEndTimeStamp) { + return `${formatToShortDateTime(epoch.epochStartTimeStamp * 1000)} - ${formatToShortDateTime(epoch.epochEndTimeStamp * 1000)} (UTC)`; + } + if (epoch.epochStartTimeStamp) { + return `${formatToMonthDay(epoch.epochStartTimeStamp * 1000)} (UTC)`; + } + return 'NA'; + }, [epoch]); + + return ( + + {formatToMonthDay(epoch.epochEndTimeStamp * 1000)} +   + + + Epoch duration + + + {timePeriod} + + + End of epoch transaction {UNICODE_SYMBOLS.EXTERNAL_LINK} + + + } + > + + + + ); +}; + +type ContractRewardsHistoryProps = { contract: StakingReward }; +const ContractRewards = ({ contract }: ContractRewardsHistoryProps) => ( + + + {contract.name} + + + {contract.history.map((epoch) => { + const currentEpochReward = epoch.reward + ? `~${balanceFormat(epoch.reward, 2)} OLAS` + : 'NA'; + + return ( + + + + + + {currentEpochReward} + + + {epoch.earned ? : } + + + ); + })} + +); + +export const RewardsHistory = () => { + const { rewards, isError, isLoading, isFetching, refetch } = + useRewardsHistory(); + const { goto } = usePageState(); + + const history = useMemo(() => { + if (isLoading || isFetching) return ; + if (isError) return ; + if (!rewards) return ; + if (rewards.length === 0) return ; + return ( + + {rewards.map((reward) => ( + + ))} + + ); + }, [isLoading, isFetching, isError, rewards, refetch]); + + return ( + + } + noBodyPadding="true" + extra={ + - - ); -}; diff --git a/frontend/constants/urls.ts b/frontend/constants/urls.ts index 9336660f..ccced30c 100644 --- a/frontend/constants/urls.ts +++ b/frontend/constants/urls.ts @@ -2,6 +2,10 @@ export const BACKEND_URL: string = `http://localhost:${process.env.NODE_ENV === export const COW_SWAP_GNOSIS_XDAI_OLAS_URL: string = 'https://swap.cow.fi/#/100/swap/WXDAI/OLAS'; +export const FAQ_URL = 'https://olas.network/operate#faq'; + +// discord export const SUPPORT_URL = 'https://discord.com/channels/899649805582737479/1244588374736502847'; -export const FAQ_URL = 'https://olas.network/operate#faq'; +export const DISCORD_TICKET_URL = + 'https://discord.com/channels/899649805582737479/1245674435160178712/1263815577240076308'; diff --git a/frontend/enums/PageState.ts b/frontend/enums/PageState.ts index a2cdc66f..3b856aa8 100644 --- a/frontend/enums/PageState.ts +++ b/frontend/enums/PageState.ts @@ -8,4 +8,5 @@ export enum Pages { ManageStaking, YourWalletBreakdown, RewardsHistory, + AddBackupWalletViaSafe, } diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index f39b9fd8..a448ceda 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -1,8 +1,9 @@ import { useEffect, useMemo } from 'react'; -import { HelpAndSupport } from '@/components/HelpAndSupportPage'; import { Main } from '@/components/MainPage'; import { ManageStakingPage } from '@/components/ManageStakingPage'; +import { AddBackupWalletViaSafePage } from '@/components/Pages/AddBackupWalletViaSafePage'; +import { HelpAndSupport } from '@/components/Pages/HelpAndSupportPage'; import { RewardsHistory } from '@/components/RewardsHistory/RewardsHistory'; import { Settings } from '@/components/SettingsPage'; import { Setup } from '@/components/SetupPage'; @@ -55,6 +56,8 @@ export default function Home() { return ; case Pages.RewardsHistory: return ; + case Pages.AddBackupWalletViaSafe: + return ; default: return
; } diff --git a/frontend/styles/globals.scss b/frontend/styles/globals.scss index 5a79088c..1a06c305 100644 --- a/frontend/styles/globals.scss +++ b/frontend/styles/globals.scss @@ -200,11 +200,12 @@ button, input, select, textarea, .ant-input-suffix { .text-light { color: #4D596A !important; } - -// font color .text-light { color: #4D596A !important; } +.text-primary { + color: #7E22CE !important; +} .pointer { cursor: pointer;