diff --git a/icons/share.svg b/icons/share.svg new file mode 100644 index 0000000000..f1124e47d0 --- /dev/null +++ b/icons/share.svg @@ -0,0 +1,3 @@ + + + diff --git a/mocks/apps/ratings.ts b/mocks/apps/ratings.ts index 3eeca5b693..3a0322850d 100644 --- a/mocks/apps/ratings.ts +++ b/mocks/apps/ratings.ts @@ -6,6 +6,7 @@ export const ratings = { fields: { appId: apps[0].id, rating: 4.3, + count: 15, }, }, ], diff --git a/public/icons/name.d.ts b/public/icons/name.d.ts index eb787ac206..62b9e9732f 100644 --- a/public/icons/name.d.ts +++ b/public/icons/name.d.ts @@ -116,6 +116,7 @@ | "score/score-not-ok" | "score/score-ok" | "search" + | "share" | "social/canny" | "social/coingecko" | "social/coinmarketcap" diff --git a/types/client/marketplace.ts b/types/client/marketplace.ts index 800e7ecb6d..a6785311c7 100644 --- a/types/client/marketplace.ts +++ b/types/client/marketplace.ts @@ -29,6 +29,7 @@ export type MarketplaceAppOverview = MarketplaceAppPreview & MarketplaceAppSocia export type AppRating = { recordId: string; value: number | undefined; + count?: number; } export type MarketplaceAppWithSecurityReport = MarketplaceAppOverview & { diff --git a/ui/marketplace/FavoriteIcon.tsx b/ui/marketplace/FavoriteIcon.tsx index e2b589b98d..a424f1a6e1 100644 --- a/ui/marketplace/FavoriteIcon.tsx +++ b/ui/marketplace/FavoriteIcon.tsx @@ -9,13 +9,13 @@ type Props = { } const FavoriteIcon = ({ isFavorite, color }: Props) => { - const heartFilledColor = useColorModeValue('blue.700', 'gray.400'); - const defaultColor = isFavorite ? heartFilledColor : 'gray.400'; + const heartFilledColor = useColorModeValue('blue.600', 'blue.300'); + const defaultColor = isFavorite ? heartFilledColor : (color || 'gray.400'); return ( ); diff --git a/ui/marketplace/MarketplaceAppCard.tsx b/ui/marketplace/MarketplaceAppCard.tsx index 834255eff6..050e17c5e3 100644 --- a/ui/marketplace/MarketplaceAppCard.tsx +++ b/ui/marketplace/MarketplaceAppCard.tsx @@ -5,6 +5,8 @@ import React, { useCallback } from 'react'; import type { MarketplaceAppWithSecurityReport, ContractListTypes, AppRating } from 'types/client/marketplace'; import useIsMobile from 'lib/hooks/useIsMobile'; +import isBrowser from 'lib/isBrowser'; +import CopyToClipboard from 'ui/shared/CopyToClipboard'; import AppSecurityReport from './AppSecurityReport'; import FavoriteIcon from './FavoriteIcon'; @@ -168,7 +170,7 @@ const MarketplaceAppCard = ({ > More info - + } + ml={ 2 } + /> + diff --git a/ui/marketplace/MarketplaceAppModal.tsx b/ui/marketplace/MarketplaceAppModal.tsx index 0eb9906480..430766b1ac 100644 --- a/ui/marketplace/MarketplaceAppModal.tsx +++ b/ui/marketplace/MarketplaceAppModal.tsx @@ -10,7 +10,9 @@ import { ContractListTypes } from 'types/client/marketplace'; import config from 'configs/app'; import useIsMobile from 'lib/hooks/useIsMobile'; import { nbsp } from 'lib/html-entities'; +import isBrowser from 'lib/isBrowser'; import * as mixpanel from 'lib/mixpanel/index'; +import CopyToClipboard from 'ui/shared/CopyToClipboard'; import type { IconName } from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg'; @@ -113,6 +115,8 @@ const MarketplaceAppModal = ({ } catch (err) {} } + const iconColor = useColorModeValue('blue.600', 'gray.400'); + return ( } + icon={ } + /> + + diff --git a/ui/marketplace/MarketplaceAppTopBar.tsx b/ui/marketplace/MarketplaceAppTopBar.tsx index 5cfc939208..8f9c233620 100644 --- a/ui/marketplace/MarketplaceAppTopBar.tsx +++ b/ui/marketplace/MarketplaceAppTopBar.tsx @@ -53,7 +53,7 @@ const MarketplaceAppTopBar = ({ appId, data, isLoading, securityReport }: Props) return ( <> - + { !isMobile && } diff --git a/ui/marketplace/MarketplaceList.tsx b/ui/marketplace/MarketplaceList.tsx index 19cf53722b..0d4706c5c3 100644 --- a/ui/marketplace/MarketplaceList.tsx +++ b/ui/marketplace/MarketplaceList.tsx @@ -45,10 +45,7 @@ const MarketplaceList = ({ return apps.length > 0 ? ( <> { rating?.value } + { rating?.count && ({ rating?.count }) } ) } @@ -58,6 +59,7 @@ const Rating = ({ void; @@ -24,7 +25,7 @@ const getTooltipText = (canRate: boolean | undefined) => { }; const TriggerButton = ( - { rating, fullView, isActive, onClick, canRate }: Props, + { rating, count, fullView, isActive, onClick, canRate }: Props, ref: React.ForwardedRef, ) => { const textColor = useColorModeValue('blackAlpha.800', 'whiteAlpha.800'); @@ -75,8 +76,9 @@ const TriggerButton = ( /> ) } { (rating && !fullView) ? ( - + { rating } + ({ count }) ) : ( 'Rate it!' diff --git a/ui/marketplace/Rating/useRatings.tsx b/ui/marketplace/Rating/useRatings.tsx index 7c4ff1aa9f..663b1b3667 100644 --- a/ui/marketplace/Rating/useRatings.tsx +++ b/ui/marketplace/Rating/useRatings.tsx @@ -28,11 +28,12 @@ export type RateFunction = ( function formatRatings(data: Airtable.Records) { return data.reduce((acc: Record, record) => { - const fields = record.fields as { appId: string | Array; rating: number | undefined }; + const fields = record.fields as { appId: string | Array; rating: number | undefined; count?: number }; const appId = Array.isArray(fields.appId) ? fields.appId[0] : fields.appId; acc[appId] = { recordId: record.id, value: fields.rating, + count: fields.count, }; return acc; }, {}); @@ -63,7 +64,7 @@ export default function useRatings() { return; } try { - const data = await airtable('apps_ratings').select({ fields: [ 'appId', 'rating' ] }).all(); + const data = await airtable('apps_ratings').select({ fields: [ 'appId', 'rating', 'count' ] }).all(); const ratings = formatRatings(data); setRatings(ratings); } catch (error) { diff --git a/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_dark-color-mode_base-view-dark-mode-1.png index e917e49cca..493887e925 100644 Binary files a/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_dark-color-mode_base-view-dark-mode-1.png and b/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_dark-color-mode_base-view-dark-mode-1.png differ diff --git a/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_default_base-view-dark-mode-1.png b/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_default_base-view-dark-mode-1.png index b714324fa3..0e0c241482 100644 Binary files a/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_default_base-view-dark-mode-1.png and b/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_default_base-view-dark-mode-1.png differ diff --git a/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_default_mobile-base-view-1.png b/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_default_mobile-base-view-1.png index 11b310da3c..61f498f479 100644 Binary files a/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_default_mobile-base-view-1.png and b/ui/marketplace/__screenshots__/MarketplaceAppModal.pw.tsx_default_mobile-base-view-1.png differ diff --git a/ui/pages/Marketplace.pw.tsx b/ui/pages/Marketplace.pw.tsx index 1b790db9b3..49f046ace9 100644 --- a/ui/pages/Marketplace.pw.tsx +++ b/ui/pages/Marketplace.pw.tsx @@ -22,7 +22,7 @@ test.beforeEach(async({ mockConfigResponse, mockEnvs, mockAssetResponse, page }) await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', MARKETPLACE_CONFIG_URL, JSON.stringify(appsMock)); await mockConfigResponse('NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL', MARKETPLACE_SECURITY_REPORTS_URL, JSON.stringify(securityReportsMock)); await Promise.all(appsMock.map(app => mockAssetResponse(app.logo, './playwright/mocks/image_s.jpg'))); - await page.route('https://api.airtable.com/v0/test/apps_ratings?fields%5B%5D=appId&fields%5B%5D=rating', (route) => route.fulfill({ + await page.route('https://api.airtable.com/v0/test/apps_ratings?fields%5B%5D=appId&fields%5B%5D=rating&fields%5B%5D=count', (route) => route.fulfill({ status: 200, body: JSON.stringify(ratingsMock), })); diff --git a/ui/pages/MarketplaceApp.pw.tsx b/ui/pages/MarketplaceApp.pw.tsx index bebab02457..e17d554f3d 100644 --- a/ui/pages/MarketplaceApp.pw.tsx +++ b/ui/pages/MarketplaceApp.pw.tsx @@ -35,7 +35,7 @@ const testFn: Parameters[1] = async({ render, mockConfigResponse, m Method: 'eth_chainId', ReturnType: numberToHex(Number(config.chain.id)), }); - await page.route('https://api.airtable.com/v0/test/apps_ratings?fields%5B%5D=appId&fields%5B%5D=rating', (route) => route.fulfill({ + await page.route('https://api.airtable.com/v0/test/apps_ratings?fields%5B%5D=appId&fields%5B%5D=rating&fields%5B%5D=count', (route) => route.fulfill({ status: 200, body: JSON.stringify(ratingsMock), })); diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_base-view-dark-mode-1.png index cd2c0136f8..829eba61cc 100644 Binary files a/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_base-view-dark-mode-1.png and b/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_base-view-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-banner-dark-mode-1.png b/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-banner-dark-mode-1.png index 477686b33a..1f3138fb98 100644 Binary files a/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-banner-dark-mode-1.png and b/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-banner-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-featured-app-dark-mode-1.png b/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-featured-app-dark-mode-1.png index 36c19db3e2..e10739c05a 100644 Binary files a/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-featured-app-dark-mode-1.png and b/ui/pages/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-featured-app-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_base-view-dark-mode-1.png b/ui/pages/__screenshots__/Marketplace.pw.tsx_default_base-view-dark-mode-1.png index ef79ab8616..e97deccb80 100644 Binary files a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_base-view-dark-mode-1.png and b/ui/pages/__screenshots__/Marketplace.pw.tsx_default_base-view-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-base-view-1.png b/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-base-view-1.png index f8a419b6c1..a24009cbcf 100644 Binary files a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-base-view-1.png and b/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-base-view-1.png differ diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-with-banner-1.png b/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-with-banner-1.png index a1aea97f7e..8facaeda8c 100644 Binary files a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-with-banner-1.png and b/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-with-banner-1.png differ diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-with-featured-app-1.png b/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-with-featured-app-1.png index 17d2c4035b..105ec24bf5 100644 Binary files a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-with-featured-app-1.png and b/ui/pages/__screenshots__/Marketplace.pw.tsx_default_mobile-with-featured-app-1.png differ diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_with-banner-dark-mode-1.png b/ui/pages/__screenshots__/Marketplace.pw.tsx_default_with-banner-dark-mode-1.png index 4b9e369833..9eb7a71870 100644 Binary files a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_with-banner-dark-mode-1.png and b/ui/pages/__screenshots__/Marketplace.pw.tsx_default_with-banner-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_with-featured-app-dark-mode-1.png b/ui/pages/__screenshots__/Marketplace.pw.tsx_default_with-featured-app-dark-mode-1.png index ad3a840796..d54f84e16e 100644 Binary files a/ui/pages/__screenshots__/Marketplace.pw.tsx_default_with-featured-app-dark-mode-1.png and b/ui/pages/__screenshots__/Marketplace.pw.tsx_default_with-featured-app-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_dark-color-mode_base-view-dark-mode-1.png index 253979b7c2..afa7b3e39e 100644 Binary files a/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_dark-color-mode_base-view-dark-mode-1.png and b/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_dark-color-mode_base-view-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_default_base-view-dark-mode-1.png b/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_default_base-view-dark-mode-1.png index 5867526a56..71cd5a6fe3 100644 Binary files a/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_default_base-view-dark-mode-1.png and b/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_default_base-view-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_default_mobile-base-view-1.png b/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_default_mobile-base-view-1.png index 56da5e5830..3a6167f61b 100644 Binary files a/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_default_mobile-base-view-1.png and b/ui/pages/__screenshots__/MarketplaceApp.pw.tsx_default_mobile-base-view-1.png differ diff --git a/ui/shared/CopyToClipboard.tsx b/ui/shared/CopyToClipboard.tsx index f241913760..c17a16092e 100644 --- a/ui/shared/CopyToClipboard.tsx +++ b/ui/shared/CopyToClipboard.tsx @@ -1,6 +1,7 @@ import { IconButton, Tooltip, useClipboard, chakra, useDisclosure, Skeleton, useColorModeValue } from '@chakra-ui/react'; import React, { useEffect, useState } from 'react'; +import type { IconName } from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg'; export interface Props { @@ -10,14 +11,18 @@ export interface Props { onClick?: (event: React.MouseEvent) => void; size?: number; type?: 'link'; + icon?: IconName; + variant?: string; + colorScheme?: string; } -const CopyToClipboard = ({ text, className, isLoading, onClick, size = 5, type }: Props) => { +const CopyToClipboard = ({ text, className, isLoading, onClick, size = 5, type, icon, variant = 'simple', colorScheme }: Props) => { const { hasCopied, onCopy } = useClipboard(text, 1000); const [ copied, setCopied ] = useState(false); // have to implement controlled tooltip because of the issue - https://github.com/chakra-ui/chakra-ui/issues/7107 const { isOpen, onOpen, onClose } = useDisclosure(); const iconColor = useColorModeValue('gray.400', 'gray.500'); + const iconName = icon || (type === 'link' ? 'link' : 'copy'); useEffect(() => { if (hasCopied) { @@ -40,10 +45,11 @@ const CopyToClipboard = ({ text, className, isLoading, onClick, size = 5, type } } + icon={ } boxSize={ size } color={ iconColor } - variant="simple" + variant={ variant } + colorScheme={ colorScheme } display="inline-block" flexShrink={ 0 } onClick={ handleClick }