diff --git a/src/api/txHistory.ts b/src/api/txHistory.ts new file mode 100644 index 00000000..059a7c6c --- /dev/null +++ b/src/api/txHistory.ts @@ -0,0 +1,56 @@ +import { sendGetRequest } from './utils' + +type GetTxHistoryQueue = { + address: string + pageSize: number + offset: number +} + +export const getTxHistoryQueue = async ({ + address, + pageSize, + offset, +}: GetTxHistoryQueue) => + sendGetRequest({ + params: { + url: 'tx/history/queue', + config: { + params: { + address, + pageSize, + offset, + }, + }, + }, + onFailReturnedValue: undefined, + onFailedText: `Failed to get tx history by address ${address}`, + }) + +type GetTxHistory = GetTxHistoryQueue & { + networks: string[] + events: string[] +} + +export const getTxHistory = async ({ + address, + pageSize, + offset, + networks, + events, +}: GetTxHistory) => + sendGetRequest({ + params: { + url: 'tx/history', + config: { + params: { + address, + pageSize, + offset, + networks, + events, + }, + }, + }, + onFailReturnedValue: undefined, + onFailedText: `Failed to get tx history by address ${address}`, + }) diff --git a/src/assets/icons/events.svg b/src/assets/icons/events.svg new file mode 100644 index 00000000..ffb297e7 --- /dev/null +++ b/src/assets/icons/events.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/icons/received-big.svg b/src/assets/icons/received-big.svg new file mode 100644 index 00000000..7791726e --- /dev/null +++ b/src/assets/icons/received-big.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/icons/received.svg b/src/assets/icons/received.svg new file mode 100644 index 00000000..fe5350e9 --- /dev/null +++ b/src/assets/icons/received.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/sent-big.svg b/src/assets/icons/sent-big.svg new file mode 100644 index 00000000..f328ec86 --- /dev/null +++ b/src/assets/icons/sent-big.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/icons/sent.svg b/src/assets/icons/sent.svg new file mode 100644 index 00000000..1af8d14a --- /dev/null +++ b/src/assets/icons/sent.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/components/accountDashboard/index.tsx b/src/components/accountDashboard/index.tsx index 18e257da..c3eb28fd 100644 --- a/src/components/accountDashboard/index.tsx +++ b/src/components/accountDashboard/index.tsx @@ -3,6 +3,7 @@ import { Skeleton, Tooltip } from 'antd' import { MutedSpan } from '../utils/MutedText' import styles from './Index.module.sass' import { + useCurrentAccount, useMyBalances, useMyExtensionAccount, } from '../providers/MyExtensionAccountsContext' @@ -10,6 +11,7 @@ import { BalanceView } from '../homePage/address-views/utils/index' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { BIGNUMBER_ZERO } from '../../config/app/consts' +import { useGetTableData } from '../table/balancesTable/utils/useGetTableData' type TotalSectionProps = { title: string @@ -58,6 +60,8 @@ export const AccountDashboard = () => { const { t } = useTranslation() const balances = useMyBalances() const { refreshBalances } = useMyExtensionAccount() + const currentAddresses = useCurrentAccount() + useGetTableData(currentAddresses, 'chains') const { freeChainBalances, diff --git a/src/components/chat/ChatFloatingModal.module.sass b/src/components/chat/ChatFloatingModal.module.sass index 576334d5..6738e2e2 100644 --- a/src/components/chat/ChatFloatingModal.module.sass +++ b/src/components/chat/ChatFloatingModal.module.sass @@ -21,7 +21,7 @@ padding: $space_small $space_normal // To offset optical illusion from the grill icon padding-left: $space_small - border-radius: 32px + border-radius: 32px !important font-size: 1rem color: white !important display: flex diff --git a/src/components/creatorsStaking/Banner/calculateApr.tsx b/src/components/creatorsStaking/Banner/calculateApr.tsx index 938e9b7f..368cf2b6 100644 --- a/src/components/creatorsStaking/Banner/calculateApr.tsx +++ b/src/components/creatorsStaking/Banner/calculateApr.tsx @@ -2,7 +2,7 @@ import { useMemo } from 'react' import { useGeneralEraInfo } from 'src/rtk/features/creatorStaking/generalEraInfo/generalEraInfoHooks' import BN from 'bignumber.js' import { convertToBalanceWithDecimal } from '@subsocial/utils' -import { useGetDecimalsAndSymbolByNetwork } from 'src/components/utils/useGetDecimalsAndSymbolByNetwork' +import { useGetChainDataByNetwork } from 'src/components/utils/useGetDecimalsAndSymbolByNetwork' const blockReward = new BN(6) const eraReward = blockReward.multipliedBy(new BN(7200)) @@ -11,7 +11,7 @@ const backerPercent = new BN(0.6) export const useCalculateApr = () => { const generalEraInfo = useGeneralEraInfo() - const { decimal } = useGetDecimalsAndSymbolByNetwork('subsocial') + const { decimal } = useGetChainDataByNetwork('subsocial') const { info, loading } = generalEraInfo || {} diff --git a/src/components/creatorsStaking/CreatorDashboard/DashboardCards.tsx b/src/components/creatorsStaking/CreatorDashboard/DashboardCards.tsx index d9f57f05..de8b23a2 100644 --- a/src/components/creatorsStaking/CreatorDashboard/DashboardCards.tsx +++ b/src/components/creatorsStaking/CreatorDashboard/DashboardCards.tsx @@ -1,6 +1,6 @@ import { FormatBalance } from 'src/components/common/balances' import ValueOrSkeleton from '../utils/ValueOrSkeleton' -import { useGetDecimalsAndSymbolByNetwork } from 'src/components/utils/useGetDecimalsAndSymbolByNetwork' +import { useGetChainDataByNetwork } from 'src/components/utils/useGetDecimalsAndSymbolByNetwork' import { useEraStakesByIds } from 'src/rtk/features/creatorStaking/eraStake/eraStakeHooks' import { useGeneralEraInfo } from 'src/rtk/features/creatorStaking/generalEraInfo/generalEraInfoHooks' import DashboardCard from '../utils/DashboardCard' @@ -18,7 +18,7 @@ type DashboardCardsProps = { const DashboardCards = ({ creators }: DashboardCardsProps) => { const { decimal, tokenSymbol: symbol } = - useGetDecimalsAndSymbolByNetwork('subsocial') + useGetChainDataByNetwork('subsocial') const myAddress = useMyAddress() const creatorRewards = useCreatorRewards(myAddress) const eraInfo = useGeneralEraInfo() diff --git a/src/components/creatorsStaking/Creators/CreatorCard.tsx b/src/components/creatorsStaking/Creators/CreatorCard.tsx index c9853c7d..082876f0 100644 --- a/src/components/creatorsStaking/Creators/CreatorCard.tsx +++ b/src/components/creatorsStaking/Creators/CreatorCard.tsx @@ -10,7 +10,7 @@ import { useRef, useState } from 'react' import StakingModal, { StakingModalVariant } from './modals/StakeModal' import ValueOrSkeleton from '../utils/ValueOrSkeleton' import { ContactInfo } from '../utils/socialLinks' -import { useGetDecimalsAndSymbolByNetwork } from 'src/components/utils/useGetDecimalsAndSymbolByNetwork' +import { useGetChainDataByNetwork } from 'src/components/utils/useGetDecimalsAndSymbolByNetwork' import { CreatorPreview } from '../utils/CreatorPreview' import { useModalContext } from '../contexts/ModalContext' import Button from '../tailwind-components/Button' @@ -114,7 +114,7 @@ const CreatorCard = ({ spaceId }: CreatorCardProps) => { const { amount, setAmount } = useModalContext() const creatorSpaceEntity = useCreatorSpaceById(spaceId) // const eraStake = useEraStakesById(spaceId, era) - const { decimal, tokenSymbol } = useGetDecimalsAndSymbolByNetwork('subsocial') + const { decimal, tokenSymbol } = useGetChainDataByNetwork('subsocial') const backerInfo = useBackerInfo(spaceId, myAddress) const [ openAboutModal, setOpenAboutModal ] = useState(false) const [ openStakeModal, setOpenStakeModal ] = useState(false) diff --git a/src/components/creatorsStaking/Creators/modals/MoveStakeModal.tsx b/src/components/creatorsStaking/Creators/modals/MoveStakeModal.tsx index 9b6153a9..5a6b44c1 100644 --- a/src/components/creatorsStaking/Creators/modals/MoveStakeModal.tsx +++ b/src/components/creatorsStaking/Creators/modals/MoveStakeModal.tsx @@ -11,7 +11,6 @@ import { AmountInput } from './AmountInput' import { useBackerInfo } from '@/rtk/features/creatorStaking/backerInfo/backerInfoHooks' import { useMyAddress } from '@/components/providers/MyExtensionAccountsContext' import { FormatBalance } from '@/components/common/balances' -import { useGetDecimalsAndSymbolByNetwork } from '@/components/utils/useGetDecimalsAndSymbolByNetwork' import { MoveStakeTxButton } from './TxButtons' import { balanceWithDecimal, @@ -21,6 +20,7 @@ import { BIGNUMBER_ZERO } from '@/config/app/consts' import BN from 'bignumber.js' import UserIcon from '@/assets/icons/user-icon.svg' import useRedirectToCreatorsPage from '../../hooks/useRedirectToCreatorsPage' +import { useGetChainDataByNetwork } from '@/components/utils/useGetDecimalsAndSymbolByNetwork' type MoveStakeModalProps = { open: boolean @@ -35,7 +35,7 @@ const MoveStakeModal = ({ }: MoveStakeModalProps) => { const myAddress = useMyAddress() const creatorsList = useCreatorsList() - const { decimal, tokenSymbol } = useGetDecimalsAndSymbolByNetwork('subsocial') + const { decimal, tokenSymbol } = useGetChainDataByNetwork('subsocial') const redirectToCreatorsPage = useRedirectToCreatorsPage() const spaceIds = creatorsList?.map((item) => item.creator.spaceId) diff --git a/src/components/creatorsStaking/Creators/modals/StakeModal.tsx b/src/components/creatorsStaking/Creators/modals/StakeModal.tsx index 9cccd75a..7df62744 100644 --- a/src/components/creatorsStaking/Creators/modals/StakeModal.tsx +++ b/src/components/creatorsStaking/Creators/modals/StakeModal.tsx @@ -9,7 +9,7 @@ import React, { useEffect, useState, ChangeEvent } from 'react' import { useBackerInfo } from 'src/rtk/features/creatorStaking/backerInfo/backerInfoHooks' import { FormatBalance } from 'src/components/common/balances' import { StakeOrIncreaseTxButton, UnstakeTxButton } from './TxButtons' -import { useGetDecimalsAndSymbolByNetwork } from 'src/components/utils/useGetDecimalsAndSymbolByNetwork' +import { useGetChainDataByNetwork } from 'src/components/utils/useGetDecimalsAndSymbolByNetwork' import Checkbox from '../../tailwind-components/Checkbox' import { linkTextStyles } from '../../tailwind-components/LinkText' import store from 'store' @@ -80,7 +80,7 @@ type CurrentStakeProps = { const CurrentStake = ({ spaceId }: CurrentStakeProps) => { const myAddress = useMyAddress() const backerInfo = useBackerInfo(spaceId, myAddress) - const { decimal, tokenSymbol } = useGetDecimalsAndSymbolByNetwork('subsocial') + const { decimal, tokenSymbol } = useGetChainDataByNetwork('subsocial') const { info } = backerInfo || {} @@ -176,7 +176,7 @@ const StakingModal = ({ const stakingConsts = useStakingConsts() - const { decimal, tokenSymbol } = useGetDecimalsAndSymbolByNetwork('subsocial') + const { decimal, tokenSymbol } = useGetChainDataByNetwork('subsocial') const { space } = creatorSpaceEntity || {} diff --git a/src/components/creatorsStaking/Creators/modals/TxButtons.tsx b/src/components/creatorsStaking/Creators/modals/TxButtons.tsx index 8f86a63b..b6d55d84 100644 --- a/src/components/creatorsStaking/Creators/modals/TxButtons.tsx +++ b/src/components/creatorsStaking/Creators/modals/TxButtons.tsx @@ -29,7 +29,7 @@ import { useModalContext } from '../../contexts/ModalContext' import store from 'store' import { useSendEvent } from '@/components/providers/AnalyticContext' import getAmountRange from '../../utils/getAmountRangeForAnalytics' -import { useGetDecimalsAndSymbolByNetwork } from '@/components/utils/useGetDecimalsAndSymbolByNetwork' +import { useGetChainDataByNetwork } from '@/components/utils/useGetDecimalsAndSymbolByNetwork' export type CommonTxButtonProps = { amount?: string @@ -129,7 +129,7 @@ function StakingTxButton ({ export function StakeOrIncreaseTxButton (props: CommonTxButtonProps) { const myAddress = useMyAddress() const sendEvent = useSendEvent() - const { decimal } = useGetDecimalsAndSymbolByNetwork('subsocial') + const { decimal } = useGetChainDataByNetwork('subsocial') const { currencyBalance: balancesByCurrency } = useBalancesByNetwork({ address: myAddress, @@ -161,7 +161,7 @@ export function UnstakeTxButton (props: CommonTxButtonProps) { const backerInfo = useBackerInfo(props.spaceId, myAddress) const { info } = backerInfo || {} const sendEvent = useSendEvent() - const { decimal } = useGetDecimalsAndSymbolByNetwork('subsocial') + const { decimal } = useGetChainDataByNetwork('subsocial') const { totalStaked } = info || {} diff --git a/src/components/creatorsStaking/MyStaking/MyRewards.tsx b/src/components/creatorsStaking/MyStaking/MyRewards.tsx index 2906991c..8d4ddc40 100644 --- a/src/components/creatorsStaking/MyStaking/MyRewards.tsx +++ b/src/components/creatorsStaking/MyStaking/MyRewards.tsx @@ -13,7 +13,7 @@ import { import ValueOrSkeleton from '../utils/ValueOrSkeleton' import ClaimRewardsTxButton from './ClaimRewardsTxButton' import DashboardCard from '../utils/DashboardCard' -import { useGetDecimalsAndSymbolByNetwork } from 'src/components/utils/useGetDecimalsAndSymbolByNetwork' +import { useGetChainDataByNetwork } from 'src/components/utils/useGetDecimalsAndSymbolByNetwork' type RestakeButtonProps = { restake: boolean @@ -44,7 +44,7 @@ const MyRewards = () => { const myAddress = useMyAddress() const creatorsList = useCreatorsList() const { decimal, tokenSymbol: symbol } = - useGetDecimalsAndSymbolByNetwork('subsocial') + useGetChainDataByNetwork('subsocial') const creatorsSpaceIds = useMemo( () => creatorsList?.map((creator) => creator.id), diff --git a/src/components/creatorsStaking/MyStaking/Unstaking.tsx b/src/components/creatorsStaking/MyStaking/Unstaking.tsx index 1bb32816..7d417399 100644 --- a/src/components/creatorsStaking/MyStaking/Unstaking.tsx +++ b/src/components/creatorsStaking/MyStaking/Unstaking.tsx @@ -8,7 +8,7 @@ import { useGeneralEraInfo } from 'src/rtk/features/creatorStaking/generalEraInf import BN from 'bignumber.js' import { useStakingContext } from 'src/components/staking/collators/StakingContext' import { BIGNUMBER_ZERO } from 'src/config/app/consts' -import { useGetDecimalsAndSymbolByNetwork } from 'src/components/utils/useGetDecimalsAndSymbolByNetwork' +import { useGetChainDataByNetwork } from 'src/components/utils/useGetDecimalsAndSymbolByNetwork' import { useGetNextEraTime } from '../hooks/useGetNextEraTime' import { useResponsiveSize } from 'src/components/responsive' import { AiOutlineCheckCircle } from 'react-icons/ai' @@ -66,7 +66,7 @@ const TimeRemaining = ({ unlockEra, className }: TimeRemainingProps) => { const Unstaking = () => { const myAddress = useMyAddress() - const { decimal, tokenSymbol } = useGetDecimalsAndSymbolByNetwork('subsocial') + const { decimal, tokenSymbol } = useGetChainDataByNetwork('subsocial') const { isMobile } = useResponsiveSize() const columns: Column[] = [ diff --git a/src/components/creatorsStaking/utils/StakingInfoBanner.tsx b/src/components/creatorsStaking/utils/StakingInfoBanner.tsx index 8c27c360..0016aaf5 100644 --- a/src/components/creatorsStaking/utils/StakingInfoBanner.tsx +++ b/src/components/creatorsStaking/utils/StakingInfoBanner.tsx @@ -3,7 +3,7 @@ import Button from '../tailwind-components/Button' import { useSendEvent } from '@/components/providers/AnalyticContext' import { useBackerLedger } from '@/rtk/features/creatorStaking/backerLedger/backerLedgerHooks' import { useMyAddress } from '@/components/providers/MyExtensionAccountsContext' -import { useGetDecimalsAndSymbolByNetwork } from '@/components/utils/useGetDecimalsAndSymbolByNetwork' +import { useGetChainDataByNetwork } from '@/components/utils/useGetDecimalsAndSymbolByNetwork' import getAmountRange from './getAmountRangeForAnalytics' import { useResponsiveSize } from '@/components/responsive' import BN from 'bignumber.js' @@ -13,7 +13,7 @@ const StakingInfoBanner = () => { const myAddress = useMyAddress() const sendEvent = useSendEvent() const backerLedger = useBackerLedger(myAddress) - const { decimal } = useGetDecimalsAndSymbolByNetwork('subsocial') + const { decimal } = useGetChainDataByNetwork('subsocial') const { isMobile } = useResponsiveSize() const wallet = getCurrentWallet() diff --git a/src/components/homePage/HomePageLayout.tsx b/src/components/homePage/HomePageLayout.tsx index d1f0af37..da707bae 100644 --- a/src/components/homePage/HomePageLayout.tsx +++ b/src/components/homePage/HomePageLayout.tsx @@ -1,10 +1,12 @@ import NtfLayout from '../ntf/NftsLayout' import { Tabs } from 'antd' -import { useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import styles from './Index.module.sass' import BalancesTableNew from '../table/balancesTable' import { useSendEvent } from '../providers/AnalyticContext' import { useRouter } from 'next/router' +import TxHistoryLayout from '../txHistory' +import { useIsMulti } from '../providers/MyExtensionAccountsContext' type OverviewSectionProps = { addresses: string[] @@ -16,24 +18,42 @@ type HomePageTabKeys = 'portfolio' | 'history' | 'nfts' const HomePageLayout = ({ addresses }: OverviewSectionProps) => { const { query: queryParams, pathname, replace } = useRouter() + const isMulti = useIsMulti() const [ activeTab, setActiveTab ] = useState( (queryParams?.tab as HomePageTabKeys) || 'portfolio' ) const sendEvent = useSendEvent() - const tabs = [ - { - key: 'portfolio', - tab: 'Portfolio', - children: , - }, - { - key: 'nfts', - tab: 'NFTs', - children: , - }, - ] + useEffect(() => { + if (isMulti && activeTab === 'history') { + setActiveTab('portfolio') + } + }, [ isMulti, activeTab ]) + + const tabs = useMemo(() => { + const txHistoryTab = isMulti + ? {} + : { + key: 'history', + tab: 'Subsocial History', + children: , + } + + return [ + { + key: 'portfolio', + tab: 'Portfolio', + children: , + }, + { + key: 'nfts', + tab: 'NFTs', + children: , + }, + txHistoryTab, + ] + }, [ isMulti, addresses.join(',') ]) const onTabChanged = (tab: string) => { setActiveTab(tab as HomePageTabKeys) diff --git a/src/components/homePage/address-views/Name.tsx b/src/components/homePage/address-views/Name.tsx index b115f2bf..57bf5934 100644 --- a/src/components/homePage/address-views/Name.tsx +++ b/src/components/homePage/address-views/Name.tsx @@ -2,9 +2,8 @@ import React from 'react' import { AddressProps } from './utils/types' import { toShortAddress } from '../../utils/index' import { MutedSpan } from '../../utils/MutedText' -import { useExtensionName } from './utils' -import { AccountIdentities, Identity } from '../../identity/types' -import { getSubsocialIdentity } from '../../../rtk/features/identities/identitiesHooks' +import { AccountIdentities } from '../../identity/types' +import useGetProfileName from '@/hooks/useGetProfileName' type Props = AddressProps & { isShort?: boolean @@ -15,26 +14,21 @@ type Props = AddressProps & { export const Name = ({ address, - identities, isShort = true, withShortAddress, - className + className, }: Props) => { - const { name: profileName } = getSubsocialIdentity(identities) || {} + const name = useGetProfileName(address.toString()) - const extensionName = useExtensionName(address) const shortAddress = toShortAddress(address) const addressString = isShort ? shortAddress : address.toString() - const kusamaIdentity = identities?.kusama as Identity | undefined - const polkadotIdentity = identities?.polkadot as Identity | undefined - - const nonSubsocialIdentityName = kusamaIdentity?.info?.display || polkadotIdentity?.info?.display - - const name = profileName || nonSubsocialIdentityName || extensionName - const title = name ? ( - + {name} {withShortAddress && ( diff --git a/src/components/homePage/address-views/utils/index.tsx b/src/components/homePage/address-views/utils/index.tsx index 45928122..7abe8e22 100644 --- a/src/components/homePage/address-views/utils/index.tsx +++ b/src/components/homePage/address-views/utils/index.tsx @@ -112,9 +112,9 @@ export const CopyAddress = ({ return ( ) => { e.stopPropagation() + e.preventDefault() eventSource && sendEvent('full_address_copied', { eventSource }) diff --git a/src/components/interesting-accounts/AccountsLayout.tsx b/src/components/interesting-accounts/AccountsLayout.tsx index c355441f..84078428 100644 --- a/src/components/interesting-accounts/AccountsLayout.tsx +++ b/src/components/interesting-accounts/AccountsLayout.tsx @@ -185,9 +185,20 @@ const AccountsLayout = ({ } noDataDesc='No spaces yet' getKey={(account: AccountCardType) => account.account} - isCards renderItem={renderItem} - /> + > + {({ dataSource, renderItem }) => { + return ( +
+ {dataSource.map((x, i) => ( +
+ {renderItem(x, i)} +
+ ))} +
+ ) + }} + ) }, [ tabKey, selectedChain ]) diff --git a/src/components/interesting-accounts/InterestingAccounts.module.sass b/src/components/interesting-accounts/InterestingAccounts.module.sass index a4d2554b..130858f1 100644 --- a/src/components/interesting-accounts/InterestingAccounts.module.sass +++ b/src/components/interesting-accounts/InterestingAccounts.module.sass @@ -14,6 +14,14 @@ $card_image_size: 100px justify-content: center margin-bottom: $space_large +.CardGrid + display: grid + grid-template-columns: repeat(4, calc((100% / 4) - 12px)) + gap: $space_normal + +.GridItem + width: 100% + .SelectedNetwork cursor: pointer @@ -140,6 +148,10 @@ $card_image_size: 100px background-color: #fff @media (max-width: 767px) + .CardGrid + padding-left: $space_normal + padding-right: $space_normal + .CardStarButton visibility: visible !important @@ -163,8 +175,20 @@ $card_image_size: 100px right: -$space_small top: -$space_tiny + .CardGrid + grid-template-columns: 1fr + .DescriptionSection height: 5.5rem .ProfileCard padding: $space_normal + +@media (max-width: 818px) + .CardGrid + grid-template-columns: repeat(3, calc((100% / 3) - 11px)) + +@media (max-width: 600px) + .CardGrid + grid-template-columns: repeat(2, calc((100% / 2) - 8px)) + \ No newline at end of file diff --git a/src/components/interesting-accounts/InterestingAccounts.tsx b/src/components/interesting-accounts/InterestingAccounts.tsx index 3cd4ee9b..08cd0465 100644 --- a/src/components/interesting-accounts/InterestingAccounts.tsx +++ b/src/components/interesting-accounts/InterestingAccounts.tsx @@ -26,7 +26,7 @@ export const PreviewAccountsGrid: FC = () => { asPath + (asPath === '/' ? '' : '/') + 'accounts' const showAllButton = isClientSide() ? ( -
+
{t('general.showAll')} @@ -36,14 +36,13 @@ export const PreviewAccountsGrid: FC = () => { return (
- + {!isMobile && ( - + {showAllButton} )} @@ -59,7 +58,7 @@ export const PreviewAccountsGrid: FC = () => { diff --git a/src/components/list/InfiniteList.tsx b/src/components/list/InfiniteList.tsx index 6d13ccaa..c0668b92 100644 --- a/src/components/list/InfiniteList.tsx +++ b/src/components/list/InfiniteList.tsx @@ -1,14 +1,18 @@ -import DataList, { DataListProps } from './DataList' -import { useState, useCallback, useEffect } from 'react' -import { Loading, isClientSide, isServerSide } from '../utils' -import { nonEmptyArr, isEmptyArray } from '@subsocial/utils' +import { isEmptyArray, nonEmptyArr } from '@subsocial/utils' +import { useRouter } from 'next/router' +import { useCallback, useEffect, useState } from 'react' import InfiniteScroll from 'react-infinite-scroll-component' +import { + ButtonLink, + isClientSide, + isServerSide, + Loading, + tryParseInt, +} from '../utils' +import DataList, { DataListProps } from './DataList' +import { CanHaveMoreDataFn, DataListItemProps, InnerLoadMoreFn } from './types' import { useLinkParams } from './utils' -import { useRouter } from 'next/router' -import { DataListItemProps, InnerLoadMoreFn, CanHaveMoreDataFn } from './types' -import { tryParseInt, ButtonLink } from '../utils/index' import { DEFAULT_FIRST_PAGE, DEFAULT_PAGE_SIZE } from './ListData.config' -import styles from './Index.module.sass' const DEFAULT_THRESHOLD = isClientSide() ? window.innerHeight / 2 : undefined const DEFAULT_MODAL_THRESHOLD = isClientSide() @@ -22,13 +26,15 @@ type InnerInfiniteListProps = Partial> & loadingLabel?: string scrollableTarget?: string withLoadMoreLink?: boolean // Helpful for SEO + dataLoading?: boolean + dataLoadingClassName?: string canHaveMoreData: CanHaveMoreDataFn - isCards?: boolean + children?: (props: DataListProps) => JSX.Element } type InfiniteListPropsByData = Omit< InnerInfiniteListProps, - 'canHaveMoreData' + 'canHaveMoreData' | 'totalCount' > type InfiniteListByPageProps = InfiniteListPropsByData & { @@ -81,55 +87,66 @@ const InnerInfiniteList = (props: InnerInfiniteListProps) => { totalCount, canHaveMoreData, scrollableTarget, - isCards = false, + children, + dataLoading, + dataLoadingClassName, ...otherProps } = props const { query: { page: pagePath }, } = useRouter() + const hasInitialData = nonEmptyArr(dataSource) const initialPage = pagePath ? tryParseInt(pagePath.toString(), DEFAULT_FIRST_PAGE) : DEFAULT_FIRST_PAGE - const [ page, setPage ] = useState(initialPage) + const [ pageValue, setPageValue ] = useState(initialPage) const [ data, setData ] = useState(dataSource || []) - const [ loading, setLoading ] = useState(false) - const [ hasMore, setHasMore ] = useState(canHaveMoreData(dataSource, page)) + const loadingInitialState = !hasInitialData + const [ loading, setLoading ] = useState(loadingInitialState) + + const [ hasMore, setHasMore ] = useState(canHaveMoreData(dataSource, pageValue)) const getLinksParams = useLinkParams({ defaultSize: DEFAULT_PAGE_SIZE, - triggers: [ page ], + triggers: [ pageValue ], }) - const handleInfiniteOnLoad = useCallback(async (page: number) => { - setLoading(true) - const newData = await loadMore(page, DEFAULT_PAGE_SIZE) - data.push(...newData) - - setData([ ...data ]) - - if (!canHaveMoreData(newData, page)) { - setHasMore(false) - } - - setPage(page + 1) - setLoading(false) - }, []) + const handleInfiniteOnLoad = useCallback( + async (page: number) => { + setLoading(true) + const newData = await loadMore(page, DEFAULT_PAGE_SIZE) + setData([ ...data, ...(newData || []) ]) + + setLoading(false) + setHasMore(canHaveMoreData(newData, page)) + setPageValue(page + 1) + }, + [ data.length ] + ) useEffect(() => { - if (hasInitialData) return setPage(page + 1) + if (hasInitialData) return setPageValue(pageValue + 1) - handleInfiniteOnLoad(page) + handleInfiniteOnLoad(pageValue) }, []) + + if ((!hasInitialData && isEmptyArray(data) && loading) || dataLoading) + return - if (!hasInitialData && isEmptyArray(data) && loading) - return + const linkProps = getLinksParams(pageValue + 1) - const linkProps = getLinksParams(page + 1) + const dataListProps = { + ...otherProps, + totalCount, + dataSource: data, + getKey, + renderItem, + } // Default height for modals is set to 300, hence threshold for them is 150. return ( @@ -140,30 +157,19 @@ const InnerInfiniteList = (props: InnerInfiniteListProps) => { ? DEFAULT_MODAL_THRESHOLD : DEFAULT_THRESHOLD } - next={() => handleInfiniteOnLoad(page)} + next={() => handleInfiniteOnLoad(pageValue)} hasMore={hasMore} scrollableTarget={scrollableTarget} loader={} > - {isCards ? ( -
- {data.map((x, i) => ( -
- {renderItem(x, i)} -
- ))} -
- ) : ( - - )} - {withLoadMoreLink && !loading && hasMore && isServerSide() && ( - + {children ? children(dataListProps) : } + {withLoadMoreLink && !loading && hasMore && ( + Load more )} diff --git a/src/components/list/ListData.config.ts b/src/components/list/ListData.config.ts index 64be89cc..04772519 100644 --- a/src/components/list/ListData.config.ts +++ b/src/components/list/ListData.config.ts @@ -1,4 +1,4 @@ export const DEFAULT_FIRST_PAGE = 1 -export const DEFAULT_PAGE_SIZE = 20 +export const DEFAULT_PAGE_SIZE = 30 export const MAX_PAGE_SIZE = 100 export const PAGE_SIZE_OPTIONS = [ 10, 20, 50, 100 ] diff --git a/src/components/list/types.ts b/src/components/list/types.ts index c1a21641..d2d33375 100644 --- a/src/components/list/types.ts +++ b/src/components/list/types.ts @@ -1,6 +1,6 @@ export type RowKeyFn = (item: T) => string | string -export type RenderItemFn = (item: T, index: number) => React.ReactNode +export type RenderItemFn = (item: T, index: number, dataLength?: number) => React.ReactNode export type InnerLoadMoreFn = (page: number, size: number) => Promise diff --git a/src/components/onlySearch/OnlySearch.module.sass b/src/components/onlySearch/OnlySearch.module.sass index 2ad7ddd2..bf82bc4a 100644 --- a/src/components/onlySearch/OnlySearch.module.sass +++ b/src/components/onlySearch/OnlySearch.module.sass @@ -92,7 +92,6 @@ $area_width: 56rem height: 50px \:global .ant-btn-icon-only - border-radius: $border_radius_large height: 50px width: 50px diff --git a/src/components/table/balancesTable/types.tsx b/src/components/table/balancesTable/types.tsx index 52475776..797a587b 100644 --- a/src/components/table/balancesTable/types.tsx +++ b/src/components/table/balancesTable/types.tsx @@ -1,6 +1 @@ -export type BalanceVariant = 'tokens' | 'chains' - -export type MenuItem = { - key: string - label: React.ReactNode -} \ No newline at end of file +export type BalanceVariant = 'tokens' | 'chains' \ No newline at end of file diff --git a/src/components/table/balancesTable/utils/ActionPannel.tsx b/src/components/table/balancesTable/utils/ActionPannel.tsx index 7b8c982b..4b4a55be 100644 --- a/src/components/table/balancesTable/utils/ActionPannel.tsx +++ b/src/components/table/balancesTable/utils/ActionPannel.tsx @@ -11,7 +11,6 @@ import { Skeleton, Tooltip, } from 'antd' -import TableDropdownButton from './TableDropdownButton' import { BalanceVariant } from '../types' import { balanceVariantsOpt, @@ -37,6 +36,7 @@ import SwitchIcon from '@/assets/icons/switch.svg' import clsx from 'clsx' import { useMemo } from 'react' import { isDef, isEmptyArray } from '@subsocial/utils' +import TableDropdownButton from '@/components/utils/Dropdowns/Dropdown' type CommonProps = { balancesVariant: BalanceVariant diff --git a/src/components/table/balancesTable/utils/Index.module.sass b/src/components/table/balancesTable/utils/Index.module.sass index dac85309..cae7a4ea 100644 --- a/src/components/table/balancesTable/utils/Index.module.sass +++ b/src/components/table/balancesTable/utils/Index.module.sass @@ -17,6 +17,10 @@ \:global .ant-skeleton-content vertical-align: middle +.IconInLabel + border-radius: 50% + + .IconCircle text-align: center display: flex @@ -47,18 +51,6 @@ label width: 100% -.MenuStyles - @extend .CommonOverlay - min-width: 175px !important - - \:global .ant-menu-item-selected - path - fill: $color_primary - - li - &:hover - path - fill: $color_primary .MobileButtonsOverlay @extend .CommonOverlay diff --git a/src/components/table/balancesTable/utils/index.tsx b/src/components/table/balancesTable/utils/index.tsx index 4228bf5e..00a94046 100644 --- a/src/components/table/balancesTable/utils/index.tsx +++ b/src/components/table/balancesTable/utils/index.tsx @@ -90,14 +90,16 @@ export const decodeTokenId = (tokenId: string) => { type LabelWithIconProps = { label: string iconSrc: string | React.ReactNode + iconSize?: number + iconClassName?: string } -const LabelWithIcon = ({ label, iconSrc }: LabelWithIconProps) => { +export const LabelWithIcon = ({ label, iconSrc, iconSize = 16, iconClassName }: LabelWithIconProps) => { return (
-
+
{typeof iconSrc === 'string' ? ( - + ) : ( iconSrc )} diff --git a/src/components/table/utils.tsx b/src/components/table/utils.tsx index 85919ba1..0c6b490c 100644 --- a/src/components/table/utils.tsx +++ b/src/components/table/utils.tsx @@ -50,6 +50,7 @@ import { StakingCandidateInfoRecord } from '../../rtk/features/stakingCandidates import { useIdentities, getSubsocialIdentity, + useFetchIdentities, } from '../../rtk/features/identities/identitiesHooks' import { BIGNUMBER_ZERO } from '../../config/app/consts' import { useSendEvent } from '../providers/AnalyticContext' @@ -174,8 +175,10 @@ export const InnerBalancesTable = ({ return {icon} }, onExpand: (expanded, record) => { - if(expanded && record.chainName) { - sendEvent('balances_details_expanded', { network: record.chainName }) + if (expanded && record.chainName) { + sendEvent('balances_details_expanded', { + network: record.chainName, + }) } return ( record.children && @@ -441,7 +444,7 @@ export const ChainData = ({ withQr = true, isBoldName = true, desc, - eventSource + eventSource, }: ChainProps) => { const { isMobile } = useResponsiveSize() @@ -677,11 +680,13 @@ type AddressProps = { name?: string accountId: string withCopy?: boolean + showCopyIcon?: boolean isShortAddress?: boolean halfLength?: number withQr?: boolean isMonosizedFont?: boolean eventSource?: string + className?: string } export const Address = ({ @@ -690,38 +695,47 @@ export const Address = ({ withCopy = true, isShortAddress = true, halfLength = 6, + showCopyIcon = true, withQr = true, isMonosizedFont = false, - eventSource -}: AddressProps) => ( -
- {withCopy ? ( - - {isShortAddress ? toShortAddress(accountId, halfLength) : accountId} - - ) : ( - - {isShortAddress ? toShortAddress(accountId, halfLength) : accountId} - - )} - {withQr && ( - - )} -
-) + eventSource, + className, +}: AddressProps) => { + const copyText = name ? `${name} address copied` : 'Address copied' + + return ( +
+ {withCopy ? ( + + {isShortAddress ? toShortAddress(accountId, halfLength) : accountId} + + ) : ( + + {isShortAddress ? toShortAddress(accountId, halfLength) : accountId} + + )} + {withQr && ( + + )} +
+ ) +} type AccountPreviewProps = { name?: string @@ -733,9 +747,12 @@ type AccountPreviewProps = { halfLength?: number isMonosizedFont?: boolean withName?: boolean + withAvatar?: boolean withAddress?: boolean largeAvatar?: boolean eventSource?: string + nameClassName?: string + identityLoadNotRequired?: boolean } export const AccountPreview = ({ @@ -749,9 +766,13 @@ export const AccountPreview = ({ isMonosizedFont = true, withName = true, withAddress = true, + withAvatar = true, largeAvatar = false, eventSource, + nameClassName, + identityLoadNotRequired }: AccountPreviewProps) => { + useFetchIdentities([ account ], identityLoadNotRequired) const identities = useIdentities(account) const address = ( @@ -767,7 +788,11 @@ export const AccountPreview = ({ ) const accountName = ( - + ) const subsocialAvatar = getSubsocialIdentity(identities)?.image @@ -779,11 +804,13 @@ export const AccountPreview = ({ className )} > - + {withAvatar && ( + + )}
{withName && accountName} diff --git a/src/components/topMenu/TopMeny.module.sass b/src/components/topMenu/TopMeny.module.sass index abb84561..8ff378a3 100644 --- a/src/components/topMenu/TopMeny.module.sass +++ b/src/components/topMenu/TopMeny.module.sass @@ -6,7 +6,7 @@ border-style: solid border-width: 0 1px 1px 1px position: fixed !important - z-index: 1 + z-index: 11 max-height: 100% overflow-y: auto overflow-x: hidden diff --git a/src/components/txHistory/CustomDataList.tsx b/src/components/txHistory/CustomDataList.tsx new file mode 100644 index 00000000..0539bdaa --- /dev/null +++ b/src/components/txHistory/CustomDataList.tsx @@ -0,0 +1,56 @@ +import React from 'react' +import NoData from 'src/components/utils/EmptyList' +import { DataListProps } from '../list' +import { groupBy } from 'lodash' +import { Transaction } from './types' +import styles from './Index.module.sass' +import utc from 'dayjs/plugin/utc' +import dayjs from 'dayjs' + +dayjs.extend(utc) + +function CustomDataList (props: DataListProps) { + const { + dataSource, + renderItem, + getKey, + noDataDesc = null, + noDataExt, + customNoData, + } = props + + const total = dataSource.length + + const hasData = total > 0 + + const groupedData = groupBy(dataSource, (x) => + dayjs(x.timestamp).format('MMMM D, YYYY') + ) + + if (!hasData) { + return ( + <> + {customNoData || {noDataExt}} + + ) + } + + return ( +
+ {Object.entries(groupedData).map(([ date, item ], i) => { + return ( +
+
{date}
+
+ {item.map((x, i) => ( +
{renderItem(x, i, item.length)}
+ ))} +
+
+ ) + })} +
+ ) +} + +export default CustomDataList diff --git a/src/components/txHistory/Index.module.sass b/src/components/txHistory/Index.module.sass new file mode 100644 index 00000000..d85a2340 --- /dev/null +++ b/src/components/txHistory/Index.module.sass @@ -0,0 +1,117 @@ +@import 'src/styles/subsocial-vars.scss' + +.HistoryBlock + background-color: #fff + // padding: $space_normal + box-shadow: $shadow + border-radius: $border_radius_normal + margin-bottom: 0.1rem + + display: flex + flex-direction: column + gap: $space_tiny + +.TransactionsList + padding: 0 $space_normal $space_normal $space_normal + +.MenuItemAll + height: 24px + width: 24px + align-items: center + justify-content: center + background: #F0F0F0 + border-radius: 50% + +.AllEventsIcon + svg + margin-top: 1px + margin-left: 2px + +.IconBox + svg + height: 25px + width: 24px + +.AllEvents + &:global(.ant-menu-item-selected) + svg + path + stroke: $color_primary !important + + li + &:hover + path + stroke: $color_primary !important + +.TxHistoryActionBlock + position: sticky + z-index: 9 + top: 64px + background-color: #fff + border-radius: inherit + border-bottom-left-radius: 0 + border-bottom-right-radius: 0 + padding: $space_normal + +.TxHistoryButtons + display: flex + align-items: center + justify-content: space-between + gap: $space_normal + +.CommonActionButtonsParts + display: flex + align-items: center + gap: $space_tiny + +.TransactionByDateContainer + display: flex + flex-direction: column + gap: $space_tiny + +.LabelWithCount + display: flex + align-items: center + gap: $space_mini + +.DownloadButton + border-radius: 18px + + &:hover + path[stroke] + stroke: $color_primary + + &:focus + path[stroke] + stroke: $color_primary + +.LeftPart, +.RightPart + @extend .CommonActionButtonsParts + +.Date + font-size: $font_medium + font-weight: 600 + +.InfiniteListLoading + height: 300px !important + +.GroupedData + display: flex + flex-direction: column + gap: $space_big + +@media ( max-width: $max_mobile_width ) + .TransactionsList + \:global .infinite-scroll-component + margin-top: 1rem + + .HistoryBlock + gap: 0 + + .TxHistoryActionBlock + border-bottom: 1px solid #F0F0F0 + + .TransactionByDateContainer + gap: 0 + diff --git a/src/components/txHistory/filter/ListFilter.tsx b/src/components/txHistory/filter/ListFilter.tsx new file mode 100644 index 00000000..20d8cdcd --- /dev/null +++ b/src/components/txHistory/filter/ListFilter.tsx @@ -0,0 +1,66 @@ +import SelectbleDropdown, { + DropdownActionKind, +} from '@/components/utils/Dropdowns/SelectableDropdown' +import { MenuItem } from '@/components/utils/Dropdowns/types' +import styles from '../Index.module.sass' +import { LabelWithIcon } from '@/components/table/balancesTable/utils' +import { useState } from 'react' +import { useResponsiveSize } from '@/components/responsive' + +type ListFilterProps = { + menus: MenuItem[] + filters: string[] + setFilters: (filter: string[]) => void + label: string + labelImage: React.ReactNode + scrollPosition?: number +} + +const ListFilter = ({ filters, setFilters, menus, label, labelImage, scrollPosition }: ListFilterProps) => { + const [ visible, setVisible ] = useState(false) + const { isMobile } = useResponsiveSize() + + const onChange = (values: string[], kind: DropdownActionKind) => { + const newValue = values.find((x) => !filters.includes(x)) + + const isAll = newValue === 'all' + + if (kind === 'select' && values.includes('all') && !isAll) { + setFilters(values.filter((x) => x !== 'all')) + } else if (kind === 'select' && isAll) { + setFilters([ 'all' ]) + setVisible(false) + } else if (kind === 'deselect' && values.length < 1) { + setFilters([ 'all' ]) + } else { + setFilters(values) + } + + + window.scrollTo(0, isMobile ? scrollPosition || 0 : 0) + } + + return ( + <> + + + {!filters.includes('all') ? `(${filters.length})` : ''} +
+ } + /> + + ) +} + +export default ListFilter diff --git a/src/components/txHistory/filter/filterItems.tsx b/src/components/txHistory/filter/filterItems.tsx new file mode 100644 index 00000000..99c4a8c5 --- /dev/null +++ b/src/components/txHistory/filter/filterItems.tsx @@ -0,0 +1,115 @@ +import { PiShareNetworkLight } from 'react-icons/pi' +import { LabelWithIcon } from '../../table/balancesTable/utils' +import { getIconUrl } from '../../utils' +import styles from '../Index.module.sass' +import EventsIcon from '@/assets/icons/events.svg' +import clsx from 'clsx' +import SentIcon from '@/assets/icons/sent-big.svg' +import ReceivedIcon from '@/assets/icons/received-big.svg' + +export const networksVariantsWithIconOpt = [ + { + label: ( + } + /> + ), + key: 'all', + }, + { + label: ( + + ), + key: 'polkadot', + }, + { + label: ( + + ), + key: 'kusama', + }, + { + label: ( + + ), + key: 'astar', + }, + { + label: ( + + ), + key: 'moonbeam', + }, + { + label: ( + + ), + key: 'moonriver', + }, + { + label: ( + + ), + key: 'subsocial', + }, +] + +export const eventsVariantsOpt = [ + { + label: ( + } + /> + ), + key: 'all', + className: styles.AllEvents, + }, + { + label: ( + } + /> + ), + key: 'transfer_from', + }, + { + label: ( + } + /> + ), + key: 'transfer_to', + }, +] \ No newline at end of file diff --git a/src/components/txHistory/index.tsx b/src/components/txHistory/index.tsx new file mode 100644 index 00000000..530927fd --- /dev/null +++ b/src/components/txHistory/index.tsx @@ -0,0 +1,230 @@ +import { DataListProps, InfiniteListByData } from '../list' +import styles from './Index.module.sass' +import { getTxHistory } from '@/api/txHistory' +import { TransferRow } from './transactions/Transfer' +import { Transaction } from './types' +import CustomDataList from './CustomDataList' +import { useCallback, useEffect, useRef, useState } from 'react' +import useGetInitialTxHistoryData from './useGetTxHistory' +import { Button, Tooltip } from 'antd' +import { SubDate, isEmptyArray } from '@subsocial/utils' +import { LoadingOutlined, ReloadOutlined } from '@ant-design/icons' +import { MutedDiv } from '../utils/MutedText' +import { useResponsiveSize } from '../responsive' +import ListFilter from './filter/ListFilter' +import EventsIcon from '@/assets/icons/events.svg' +import { + eventsVariantsOpt, + // networksVariantsWithIconOpt, +} from './filter/filterItems' +// import { PiShareNetworkLight } from 'react-icons/pi' +import { toGenericAccountId } from '../../rtk/app/util' +import { useFetchIdentities } from '@/rtk/features/identities/identitiesHooks' + +const itemsByTxKind: Record = { + TRANSFER_FROM: TransferRow, + TRANSFER_TO: TransferRow, +} + +type LoadMoreProps = { + address: string + page: number + size: number + networks: string[] + events: string[] +} + +const loadMore = async ({ + address, + page, + size, + networks, + events, +}: LoadMoreProps) => { + const offset = (page - 1) * size + + const history = await getTxHistory({ + address, + pageSize: size, + offset, + networks, + events, + }) + + return history +} + +type TxHistoryLayoutProps = { + addresses: string[] +} + +const supportedNetowrks = [ 'subsocial' ] + +const TxHistoryLayout = ({ addresses }: TxHistoryLayoutProps) => { + // const [ networks, setNetworks ] = useState([ 'all' ]) + const [ events, setEvents ] = useState([ 'all' ]) + const address = addresses[0] + const [ refresh, setRefresh ] = useState(false) + const { isMobile } = useResponsiveSize() + const historySection = useRef(null) + + const { initialData, lastUpdateDate } = useGetInitialTxHistoryData({ + address, + refresh, + setRefresh, + }) + + const renderItem = (item: Transaction, i: number, dataLength?: number) => { + const { txKind } = item + + const Component = itemsByTxKind[txKind] + + return ( + + ) + } + + const dataLoading = isEmptyArray(initialData.txs) && !initialData.actualData + + const List = useCallback(() => { + return ( +
+ + loadMore({ + address, + page, + size, + networks: supportedNetowrks.filter((x) => x !== 'all'), + events: events.filter((x) => x !== 'all'), + }) + } + dataLoading={dataLoading} + dataLoadingClassName={styles.InfiniteListLoading} + noDataDesc='No transactions yet' + dataSource={ + supportedNetowrks.includes('all') && events.includes('all') + ? initialData.txs + : undefined + } + getKey={(data) => data.id} + renderItem={renderItem} + > + {(dataListProps) => { + return + }} + +
+ ) + }, [ + dataLoading, + address, + JSON.stringify(initialData.txs), + supportedNetowrks.join(','), + events.join(','), + ]) + + return ( +
+
+
+
+ {/* } + /> */} + } + scrollPosition={(historySection.current as any)?.offsetTop - 180} + /> +
+
+ {!isMobile && ( + + )} + +
+
+ {isMobile && ( +
+ Last update: + +
+ )} +
+ + +
+ ) +} + +type TransactionsDataListProps = { + dataListProps: DataListProps +} + +const TransactionsDataList = ({ dataListProps }: TransactionsDataListProps) => { + const addresses = dataListProps.dataSource.map((x) => + toGenericAccountId(x.senderOrTargetPublicKey) + ) + + useFetchIdentities(addresses) + + return +} + +type LastUpdateProps = { + lastUpdateDate?: Date + refresh: boolean +} + +const LastUpdate = ({ lastUpdateDate, refresh }: LastUpdateProps) => { + const [ lastUpdate, setLastUpdate ] = useState(null) + + useEffect(() => { + if (!lastUpdateDate) return + + const intervalId = setInterval(() => { + setLastUpdate(SubDate.formatDate(lastUpdateDate.getTime())) + }, 1000) + + return () => { + clearInterval(intervalId) + } + }, [ lastUpdateDate?.getTime() ]) + + return ( + +
+ + {refresh || !lastUpdateDate ? 'updating...' : lastUpdate} + +
+
+ ) +} + +export default TxHistoryLayout diff --git a/src/components/txHistory/transactions/MobileTransferModal.tsx b/src/components/txHistory/transactions/MobileTransferModal.tsx new file mode 100644 index 00000000..673edc1f --- /dev/null +++ b/src/components/txHistory/transactions/MobileTransferModal.tsx @@ -0,0 +1,158 @@ +import FloatingModal from '@/components/utils/FloatingModal' +import styles from './Transactions.module.sass' +import dayjs from 'dayjs' +import { Button } from 'antd' +import { RiArrowRightUpLine } from 'react-icons/ri' +import { useCurrentAccount } from '@/components/providers/MyExtensionAccountsContext' +import { AccountPreview, AvatarOrSkeleton } from '@/components/table/utils' +import { CopyAddress } from '../../homePage/address-views/utils/index' +import { toShortAddress } from '../../utils/index' +import Link from 'next/link' +import clsx from 'clsx' + +type TransferInfo = { + icon: string + address: string + balance: React.ReactNode + totalBalance: React.ReactNode + txKind: string + timestamp: string + extrinsicHash: string + networkName: string + subscanUrl: string +} + +type MobileTransferModalProps = { + open: boolean + closeModal: () => void + transferInfo: TransferInfo +} + +const MobileTransferModal = ({ + open, + closeModal, + transferInfo, +}: MobileTransferModalProps) => { + const { + icon, + address: senderOrTarget, + balance, + totalBalance, + txKind, + timestamp, + extrinsicHash, + networkName, + subscanUrl, + } = transferInfo + + const currentAddresses = useCurrentAccount() + + const currentAddress = currentAddresses?.[0] + + const date = dayjs(timestamp).format('MMM DD, YYYY [at] HH:mm:ss ') + + const isRecieved = txKind === 'TRANSFER_TO' + + const sender = isRecieved ? senderOrTarget : currentAddress + const recipient = isRecieved ? currentAddress : senderOrTarget + + return ( + +
+
+
+
+ {isRecieved ? '+' : '-'} + {balance} +
+
{totalBalance}
+
+
+
+ Sender + + + + + +
+
+ Network + + + {networkName} + +
+
+
+ Recipient + + + + + +
+
+ Transaction ID + + + {toShortAddress(extrinsicHash, 8)} + + +
+
{date}
+
+ + +
+
+ ) +} + +export default MobileTransferModal diff --git a/src/components/txHistory/transactions/Transactions.module.sass b/src/components/txHistory/transactions/Transactions.module.sass new file mode 100644 index 00000000..585d6d1b --- /dev/null +++ b/src/components/txHistory/transactions/Transactions.module.sass @@ -0,0 +1,126 @@ +@import 'src/styles/subsocial-vars.scss' + +.TransferRow + display: grid + grid-template-columns: 1fr 1fr 1fr + align-items: center + gap: $space_normal + + min-height: 62px + +.Tokens + font-size: $font_normal + font-weight: 600 + +.Dollars + font-size: $font_small + +.FirstCol + width: 250px + +.TransferTitle + display: flex + align-items: center + gap: $space_mini + +.RecievedTokens + color: #16A34A + +.TransactionDivider + display: flex + justify-content: flex-end + + \:global .ant-divider + margin: 0 + margin-top: $space_mini + max-width: 94.5% + min-width: auto + border-top-color: #f0f0f0 + +.TransactionIcon + position: absolute + bottom: -1px + right: -2px + +.ModalContent + display: flex + flex-direction: column + gap: $space_normal + + justify-content: space-between + height: 100% + +.TxContent + display: flex + flex-direction: column + gap: $space_normal + + margin-top: $space_normal + +.SenderBlock + display: flex + flex-direction: column + align-items: center + gap: $space_normal + + background: #F8FAFC + border-radius: 10px + padding: $space_normal + + div + width: 100% + display: flex + justify-content: space-between + +.GrayLabel + color: #888 + +.TextBlock + width: 100% + display: flex + justify-content: space-between + gap: $space_mini + + background: #F8FAFC + border-radius: 10px + padding: $space_normal + +.Tokens-Send + color: #000 + font-size: $font_big + line-height: 25.144px + font-weight: 600 + +.Tokens-Recieved + color: #16A34A + font-size: $font_big + line-height: 25.144px + font-weight: 600 + +.BalanceInDollar + font-size: $font_normal + line-height: 25.144px + + span + color: #64748B + +.Date + color: #64748B + line-height: 25.144px + margin-top: $space_tiny + +.EllipsisPreview + max-width: 168px + display: block + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + + font-weight: 600 + +@media ( max-width: $max_mobile_width ) + .TransferRow + display: flex + align-items: center + justify-content: space-between + gap: $space_mini diff --git a/src/components/txHistory/transactions/Transfer.tsx b/src/components/txHistory/transactions/Transfer.tsx new file mode 100644 index 00000000..95a7e263 --- /dev/null +++ b/src/components/txHistory/transactions/Transfer.tsx @@ -0,0 +1,315 @@ +import styles from './Transactions.module.sass' +import { + Address, + AccountPreview, + AvatarOrSkeleton, + getPrice, +} from '../../table/utils' +import { MutedDiv } from '../../utils/MutedText' +import { HiOutlineExternalLink } from 'react-icons/hi' +import clsx from 'clsx' +import { Transaction } from '../types' +import { toGenericAccountId } from '@/rtk/app/util' +import { usePrices } from '@/rtk/features/prices/pricesHooks' +import { useGetChainDataByNetwork } from '@/components/utils/useGetDecimalsAndSymbolByNetwork' +import { FormatBalance } from '@/components/common/balances' +import { convertToBalanceWithDecimal } from '@subsocial/utils' +import { BalanceView } from '../../homePage/address-views/utils/index' +import { ExternalLink } from '../../identity/utils' +import dayjs from 'dayjs' +import utc from 'dayjs/plugin/utc' +import BN from 'bignumber.js' +import { useResponsiveSize } from '@/components/responsive' +import useGetProfileName from '@/hooks/useGetProfileName' +import { AvatarSize } from 'antd/lib/avatar/SizeContext' +import SentIcon from '@/assets/icons/sent.svg' +import RecievedIcon from '@/assets/icons/received.svg' +import { Divider } from 'antd' +import Link from 'next/link' +import { useState } from 'react' +import MobileTransferModal from './MobileTransferModal' + +dayjs.extend(utc) + +const subscanLinksByNetwork: Record = { + polkadot: 'https://polkadot.subscan.io/extrinsic/', + kusama: 'https://kusama.subscan.io/extrinsic/', + astar: 'https://astar.subscan.io/extrinsic/', + moonbeam: 'https://moonbeam.subscan.io/extrinsic/', + moonriver: 'https://moonriver.subscan.io/extrinsic/', + subsocial: 'https://calamar.app/search?network=subsocial&query=', +} + +type TransferRowProps = { + item: Transaction + isLastElement?: boolean +} + +export const TransferRow = ({ item, isLastElement }: TransferRowProps) => { + const { isMobile } = useResponsiveSize() + const prices = usePrices() + const { + txKind, + amount, + senderOrTargetPublicKey, + blockchainTag, + transaction, + } = item + + const { decimal, tokenSymbol, icon, name } = useGetChainDataByNetwork( + blockchainTag.toLowerCase() + ) + + const address = toGenericAccountId(senderOrTargetPublicKey) + + const balanceWithDecimals = convertToBalanceWithDecimal(amount, decimal) + + const price = getPrice(prices, 'symbol', tokenSymbol) + + const totalBalanceBN = balanceWithDecimals.multipliedBy(price || '0') + + const totalBalance = ( + + ) + + const extrinsicHash = transaction.transferNative.extrinsicHash + + const subscanUrl = `${ + subscanLinksByNetwork[blockchainTag.toLowerCase()] + }${extrinsicHash}` + + const amountBN = new BN(amount) + + const balance = amountBN.isZero() ? ( + '0' + ) : ( + + ) + + const props = { + icon: icon, + subscanUrl: subscanUrl, + timestamp: item.timestamp, + address: address, + balance: balance, + totalBalance: totalBalance, + txKind: txKind, + extrinsicHash, + networkName: name, + } + + return ( +
+ {isMobile ? ( + + ) : ( + + )} + {!isLastElement && !isMobile && ( +
+ +
+ )} +
+ ) +} + +type DesktopTransferRowProps = { + icon: string + subscanUrl: string + timestamp: string + address: string + balance: React.ReactNode + totalBalance: React.ReactNode + txKind: string + extrinsicHash: string + networkName: string +} + +const DesktopTransfer = ({ + icon, + timestamp, + address, + balance, + subscanUrl, + totalBalance, + txKind, +}: DesktopTransferRowProps) => { + const name = useGetProfileName(address) + + const titleByKind = txKind === 'TRANSFER_TO' ? 'Received' : 'Sent' + const time = dayjs(timestamp).format('HH:mm') + + const title = ( +
+ {titleByKind} {' '} + + Transfer + + } + /> +
+ ) + + return ( +
+
+
+ +
+
{title}
+ {time} +
+
+
+
+ {txKind === 'TRANSFER_TO' ? 'From' : 'To'} + +
+ + +
+
+ +
+ +
+ ) +} + +const MobileTransfer = ({ + icon, + address, + balance, + totalBalance, + txKind, + timestamp, + extrinsicHash, + networkName, + subscanUrl, +}: DesktopTransferRowProps) => { + const [ open, setOpen ] = useState(false) + const titleByKind = txKind === 'TRANSFER_TO' ? 'Received from' : 'Sent to' + + console.log(extrinsicHash) + return ( + <> +
setOpen(true)}> +
+ +
+ {titleByKind} + + +
+
+ +
+ setOpen(false)} + transferInfo={{ + icon, + address, + balance, + totalBalance, + txKind, + timestamp, + extrinsicHash, + networkName, + subscanUrl, + }} + /> + + ) +} + +type BalancePartProps = { + txKind: string + balance: React.ReactNode + totalBalance: React.ReactNode +} + +const BalancePart = ({ txKind, balance, totalBalance }: BalancePartProps) => ( +
+
+ {txKind === 'TRANSFER_TO' ? '+' : '-'} + {balance} +
+ {totalBalance} +
+) + +type TxHistoryImageProps = { + icon: string + size: AvatarSize + txKind: string +} + +const TxHistoryImage = ({ icon, size, txKind }: TxHistoryImageProps) => { + const TxIconByTxKind = txKind === 'TRANSFER_TO' ? RecievedIcon : SentIcon + + return ( +
+ + +
+ ) +} diff --git a/src/components/txHistory/types.ts b/src/components/txHistory/types.ts new file mode 100644 index 00000000..1276ed47 --- /dev/null +++ b/src/components/txHistory/types.ts @@ -0,0 +1,14 @@ +export type Transaction = { + id: string + txKind: string + blockchainTag: string + amount: string + senderOrTargetPublicKey: string + timestamp: string + success: string + transaction: { + transferNative: { + extrinsicHash: string + } + } +} diff --git a/src/components/txHistory/useGetTxHistory.tsx b/src/components/txHistory/useGetTxHistory.tsx new file mode 100644 index 00000000..a20c1512 --- /dev/null +++ b/src/components/txHistory/useGetTxHistory.tsx @@ -0,0 +1,63 @@ +import { useEffect, useState } from 'react' +import { getTxHistoryQueue } from '../../api/txHistory' +import { Transaction } from './types' + +type InitialData = { + txs: Transaction[] + actualData: boolean +} + +type GetIniTitalTxHistoryDataProps = { + address?: string + refresh: boolean + setRefresh: (refresh: boolean) => void +} + +const defaultInitialData = { txs: [], actualData: false } + +const useGetInitialTxHistoryData = ({ + address, + refresh, + setRefresh, +}: GetIniTitalTxHistoryDataProps) => { + const [ initialData, setInitialData ] = + useState(defaultInitialData) + const [ lastUpdateDate, setLastUpdateDate ] = useState( + undefined + ) + + useEffect(() => { + setInitialData(defaultInitialData) + setRefresh(true) + }, [ address ]) + + useEffect(() => { + if (!address || !refresh) return + + const interval = setInterval(async () => { + const history: InitialData = await getTxHistoryQueue({ + address, + offset: 0, + pageSize: 30, + }) + + setInitialData(history) + + if (history?.actualData) { + clearInterval(interval) + setRefresh(false) + setLastUpdateDate(new Date()) + + return + } + }, 2000) + + return () => { + clearInterval(interval) + } + }, [ address, refresh ]) + + return { initialData: initialData || {}, lastUpdateDate } +} + +export default useGetInitialTxHistoryData diff --git a/src/components/utils/DfAvatar.tsx b/src/components/utils/DfAvatar.tsx index 35a812b7..3c7e69a6 100644 --- a/src/components/utils/DfAvatar.tsx +++ b/src/components/utils/DfAvatar.tsx @@ -18,7 +18,7 @@ export const BaseAvatar = (props: BaseAvatarProps) => { const { size = DEFAULT_AVATAR_SIZE, avatar, style, address } = props if (!avatar || isEmptyStr(avatar)) { - return + return } return diff --git a/src/components/table/balancesTable/utils/TableDropdownButton.tsx b/src/components/utils/Dropdowns/Dropdown.tsx similarity index 84% rename from src/components/table/balancesTable/utils/TableDropdownButton.tsx rename to src/components/utils/Dropdowns/Dropdown.tsx index 544ca8bc..fcfdb27b 100644 --- a/src/components/table/balancesTable/utils/TableDropdownButton.tsx +++ b/src/components/utils/Dropdowns/Dropdown.tsx @@ -1,5 +1,7 @@ import { Button, Dropdown, Menu } from 'antd' -import { MenuItem } from '../types' +import styles from './Index.module.sass' +import clsx from 'clsx' +import { MenuItem } from './types' type MenuItemsProps = { items: MenuItem[] @@ -15,7 +17,7 @@ const MenuItems = ({ defaultValue, }: MenuItemsProps) => ( onClick?.(item.key.toString())} > @@ -57,7 +59,7 @@ const TableDropdownButton = ({ placement='bottomCenter' trigger={[ 'click' ]} > - + ) } diff --git a/src/components/utils/Dropdowns/Index.module.sass b/src/components/utils/Dropdowns/Index.module.sass new file mode 100644 index 00000000..be595551 --- /dev/null +++ b/src/components/utils/Dropdowns/Index.module.sass @@ -0,0 +1,35 @@ +@import 'src/styles/subsocial-vars.scss' + +.CommonOverlay + border: none + overflow: hidden + background: #FFFFFF + box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.2) !important + border-radius: $border_radius_huge !important + +.MenuStyles + @extend .CommonOverlay + min-width: 175px !important + + \:global .ant-menu-item-selected + path + fill: $color_primary + + li + &:hover + path + fill: $color_primary + +.SelectbleMenuStyles + @extend .CommonOverlay + + min-width: 175px !important + + \:global .ant-menu-item-selected + background: none !important + + +.SelectableMenuItem + display: flex + align-items: center + justify-content: space-between \ No newline at end of file diff --git a/src/components/utils/Dropdowns/SelectableDropdown.tsx b/src/components/utils/Dropdowns/SelectableDropdown.tsx new file mode 100644 index 00000000..8bba758b --- /dev/null +++ b/src/components/utils/Dropdowns/SelectableDropdown.tsx @@ -0,0 +1,106 @@ +import { Button, Dropdown, Menu } from 'antd' +import clsx from 'clsx' +import { useEffect } from 'react' +import { MenuItem } from './types' +import styles from './Index.module.sass' +import { IoCheckmarkSharp } from 'react-icons/io5' + +export type DropdownActionKind = 'select' | 'deselect' + +type SelectableMenuItem = MenuItem & { + className?: string +} + +type MenuItemsProps = { + items: SelectableMenuItem[] + className?: string + onChange?: (keys: string[], kind: DropdownActionKind) => void + defaultValue: string + values: string[] +} + +const MenuItems = ({ + items, + className, + onChange, + values, + defaultValue, +}: MenuItemsProps) => ( + onChange?.(item.selectedKeys as string[], 'select')} + onDeselect={(item) => onChange?.(item.selectedKeys as string[], 'deselect')} + selectable + multiple + > + {items.map(({ key, label, className }) => { + const isSelected = values.includes(key) + return ( + + {label} {isSelected && } + + ) + })} + +) + +type TableDropdownButtonProps = { + label?: React.ReactNode + menu: MenuItem[] + defaultValue: string + onChange: (values: string[], kind: DropdownActionKind) => void + menuClassName?: string + values: string[] + visible: boolean + setVisible: (visible: boolean) => void +} + +const SelectbleDropdown = ({ + label, + menu, + defaultValue, + onChange, + menuClassName, + values, + visible, + setVisible +}: TableDropdownButtonProps) => { + useEffect(() => { + window.addEventListener('wheel', () => { + setVisible(false) + }) + + return () => { + window.removeEventListener('wheel', () => { + setVisible(false) + }) + } + }, []) + + return ( + setVisible(value)} + overlay={ + + } + placement='bottomCenter' + trigger={[ 'click' ]} + > + + + ) +} + +export default SelectbleDropdown diff --git a/src/components/utils/Dropdowns/types.tsx b/src/components/utils/Dropdowns/types.tsx new file mode 100644 index 00000000..8830eef1 --- /dev/null +++ b/src/components/utils/Dropdowns/types.tsx @@ -0,0 +1,4 @@ +export type MenuItem = { + key: string + label: React.ReactNode +} diff --git a/src/components/utils/FloatingModal/FloatingModal.module.sass b/src/components/utils/FloatingModal/FloatingModal.module.sass new file mode 100644 index 00000000..127574a0 --- /dev/null +++ b/src/components/utils/FloatingModal/FloatingModal.module.sass @@ -0,0 +1,159 @@ +@import 'src/styles/subsocial-vars.scss' + +.ChatFloatingWrapper + z-index: 10 + position: fixed + bottom: $space_normal + right: $space_normal + + .ChatUnreadCount + position: absolute + padding: 2px $space_tiny + top: 0 + right: 0 + font-size: $font_tiny + background: red + border-radius: 32px + transform: translate(25%, -25%) + color: white + + .ChatFloatingButton + padding: $space_small $space_normal + // To offset optical illusion from the grill icon + padding-left: $space_small + border-radius: 32px !important + font-size: 1rem + color: white !important + display: flex + justify-content: center + height: auto + align-items: center + background: linear-gradient(95.39deg, #C43333 9.79%, #F9A11E 135.53%) !important + gap: $space_tiny + + &:hover, &:focus + filter: brightness(1.1) + + img + display: block + height: 1.1rem + +.ChatContainer + z-index: 1009 + position: fixed + bottom: 0 + left: 0 + width: 100% + height: 100vh + display: flex + flex-direction: column + justify-content: flex-end + + .ChatOverlay + position: absolute + inset: 0 + width: 100% + height: 100% + background-color: rgba(0, 0, 0, .2) + transition: opacity 0.2s ease-out + opacity: 1 + + .ChatContent + height: 80vh + height: 80dvh + width: 100% + background: #F8FAFC + border-radius: $border_radius_huge + opacity: 1 + transform: translateY(0) + transition: transform 0.3s ease-in-out, opacity 0.2s ease-in-out + display: flex + flex-direction: column + + .ChatControl + display: flex + align-items: center + justify-content: center + + button + border-radius: 50% + background: #F0F1F9 + border: none + display: flex + align-items: center + justify-content: center + width: 2rem + height: 2rem + padding: 0 + font-size: 1rem + + .ChatIframe + flex: 1 + + iframe + display: block + border-radius: $border_radius_large + width: 100% + height: 100% + border: none + + &.ChatContainerHidden + pointer-events: none + + .ChatOverlay + opacity: 0 + + .ChatContent + transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out + opacity: 0 + +.Position--right + .ChatContainer + align-items: flex-end + + .ChatContent + max-width: 570px + width: 100% + position: relative + height: 100vh + height: 100dvh + + .ChatControl + padding: 0 + position: absolute + left: -40px + top: 12px + transform: rotate(-90deg) + + &.ChatContainerHidden + .ChatContent + transform: translateX(100%) + +.Position--bottom + .ChatContainer + &.ChatContainerHidden + .ChatContent + transform: translateY(100%) + +@media ( max-width: $max_mobile_width ) + .Position--right + .ChatContainer + align-items: center + + .ChatContent + max-width: none + width: 100% + position: relative + height: 90vh + height: 90dvh + + .ChatControl + padding: $space-tiny + top: 0 + left: 0 + position: relative + transform: rotate(0deg) + + &.ChatContainerHidden + .ChatContent + transform: translateY(100%) diff --git a/src/components/utils/FloatingModal/index.tsx b/src/components/utils/FloatingModal/index.tsx new file mode 100644 index 00000000..ab7ac41c --- /dev/null +++ b/src/components/utils/FloatingModal/index.tsx @@ -0,0 +1,75 @@ +import React, { useEffect } from 'react' +import styles from './FloatingModal.module.sass' +import { Button } from 'antd' +import { HiChevronDown } from 'react-icons/hi2' +import clsx from 'clsx' +import { createPortal } from 'react-dom' + +type FloatingModalProps = { + position?: 'right' | 'bottom' + open: boolean + closeModal: () => void + className?: string + children: React.ReactNode +} + +export default function FloatingModal ({ + position = 'bottom', + open, + closeModal, + children, + className +}: FloatingModalProps) { + useEffect(() => { + const close = (e: any) => { + if (e.keyCode === 27) { + closeModal() + } + } + + window.addEventListener('keydown', close) + + return () => window.removeEventListener('keydown', close) + }, []) + + useEffect(() => { + if (open) { + document.documentElement.style.overflow = 'hidden' + } else { + document.documentElement.style.overflow = 'visible' + } + }, [ open ]) + + if(!open) return null + + return ( + <> + {createPortal( +
+
+
{ + closeModal() + }} + /> +
+
+ +
+ {children} +
+
+
, + document.body, + )} + + ) +} diff --git a/src/components/utils/index.tsx b/src/components/utils/index.tsx index 7ea6b952..b7d01deb 100644 --- a/src/components/utils/index.tsx +++ b/src/components/utils/index.tsx @@ -57,13 +57,14 @@ type LoadingProps = { label?: React.ReactNode style?: React.CSSProperties center?: boolean + className?: string } -export const Loading = ({ label, style, center = true }: LoadingProps) => { +export const Loading = ({ label, style, center = true, className }: LoadingProps) => { const alignCss = center ? 'justify-content-center align-items-center' : '' return (
diff --git a/src/components/utils/useGetDecimalsAndSymbolByNetwork.tsx b/src/components/utils/useGetDecimalsAndSymbolByNetwork.tsx index b259084a..45704f88 100644 --- a/src/components/utils/useGetDecimalsAndSymbolByNetwork.tsx +++ b/src/components/utils/useGetDecimalsAndSymbolByNetwork.tsx @@ -1,15 +1,15 @@ import { useChainInfo } from 'src/rtk/features/multiChainInfo/multiChainInfoHooks' -export const useGetDecimalsAndSymbolByNetwork = (network: string) => { +export const useGetChainDataByNetwork = (network: string) => { const chainsInfo = useChainInfo() const chainInfo = chainsInfo?.[network] - const { tokenDecimals, tokenSymbols, nativeToken } = chainInfo || {} + const { tokenDecimals, tokenSymbols, nativeToken, ...otherData } = chainInfo || {} const tokenSymbol = tokenSymbols?.[0] || nativeToken const decimal = tokenDecimals?.[0] || 0 - return { decimal, tokenSymbol } + return { decimal, tokenSymbol, ...otherData } } \ No newline at end of file diff --git a/src/hooks/useGetProfileName.tsx b/src/hooks/useGetProfileName.tsx new file mode 100644 index 00000000..dd080f84 --- /dev/null +++ b/src/hooks/useGetProfileName.tsx @@ -0,0 +1,21 @@ +import { useExtensionName } from '@/components/homePage/address-views/utils' +import { Identity } from '@/components/identity/types' +import { getSubsocialIdentity, useIdentities } from '@/rtk/features/identities/identitiesHooks' + +const useGetProfileName = (address: string) => { + const identities = useIdentities(address) + const { name: profileName } = getSubsocialIdentity(identities) || {} + + const extensionName = useExtensionName(address) + + const kusamaIdentity = identities?.kusama as Identity | undefined + const polkadotIdentity = identities?.polkadot as Identity | undefined + + const nonSubsocialIdentityName = kusamaIdentity?.info?.display || polkadotIdentity?.info?.display + + const name = profileName || nonSubsocialIdentityName || extensionName + + return name +} + +export default useGetProfileName \ No newline at end of file diff --git a/src/rtk/features/identities/identitiesHooks.ts b/src/rtk/features/identities/identitiesHooks.ts index 2ddf8367..5c61426a 100644 --- a/src/rtk/features/identities/identitiesHooks.ts +++ b/src/rtk/features/identities/identitiesHooks.ts @@ -39,12 +39,12 @@ export const useIdentities = (account?: string) => (state) => selectIdentities(state, toGenericAccountId(account))?.identity ) -export const useFetchIdentities = (accounts?: string[]) => { +export const useFetchIdentities = (accounts?: string[], dataLoadNotRequired?: boolean) => { const dispatch = useAppDispatch() useEffect(() => { - if(!accounts) return + if(!accounts || dataLoadNotRequired) return const genericAccountIds = toGenericAccountIds(accounts) diff --git a/src/styles/bootstrap-utilities-4.3.1.css b/src/styles/bootstrap-utilities-4.3.1.css index 1555c450..cb4bfe6d 100644 --- a/src/styles/bootstrap-utilities-4.3.1.css +++ b/src/styles/bootstrap-utilities-4.3.1.css @@ -989,6 +989,10 @@ font-weight: 400 !important; } +.font-weight-semibold { + font-weight: 600 !important; +} + .font-weight-bold { font-weight: 700 !important; } @@ -1061,6 +1065,10 @@ color: #fff !important; } +.text-black { + color: #000 !important; +} + .text-body { color: #212529 !important; } diff --git a/src/styles/subsocial.scss b/src/styles/subsocial.scss index 8de8cea6..4150ddaf 100644 --- a/src/styles/subsocial.scss +++ b/src/styles/subsocial.scss @@ -329,8 +329,10 @@ a { /* ---------------------------------------- */ .ant-btn { - border-radius: $border_radius_large; + border-radius: 18px !important; +} +.ColoredIcon { &:hover { g[fill] { path { @@ -411,7 +413,7 @@ a { z-index: 10; // top: $height_top_menu; // height: 93.5%; - overflow: auto; + // overflow: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; @@ -507,6 +509,7 @@ a { padding: 4rem 0; padding-bottom: 0; overflow-wrap: break-word; + overflow-x: visible !important; margin: 0 auto; color: $color_font_normal; } @@ -1005,21 +1008,21 @@ hr { } .DfGreyLink { - color: $color_muted !important; + color: $color_muted; cursor: pointer; a { - color: $color_muted !important; + color: $color_muted; cursor: pointer; } } .DfGreyLink:hover { a { - color: $color_muted !important; + color: $color_muted; } - color: $color_muted !important; + color: $color_muted; } .DfMutedLink { @@ -1333,6 +1336,8 @@ hr { .ant-btn-icon-only { border-top-left-radius: 0 !important; border-bottom-left-radius: 0 !important; + border-top-right-radius: .625em !important; + border-bottom-right-radius: .625em !important; } } @@ -1549,7 +1554,7 @@ hr { .DfTopBar { position: fixed; - z-index: 11; + z-index: 12; height: $height_top_menu; width: 100%; padding: $space_normal;