Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dapp ratings #2089

Merged
merged 65 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
273f5dc
add dapp ratings
maxaleks Jul 9, 2024
c6293a4
split the rating component into multiple files
maxaleks Jul 9, 2024
9fc9d17
move trigger button to separate file and rework
maxaleks Jul 9, 2024
a630b36
add tooltip
maxaleks Jul 9, 2024
8f5c41a
add check if specific address can rate
maxaleks Jul 9, 2024
9324fa1
add rating to dapp page
maxaleks Jul 9, 2024
8d1a9fe
add toast
maxaleks Jul 9, 2024
963e447
add sorting by rating
maxaleks Jul 9, 2024
3e5b808
fix rating skeleton size
maxaleks Jul 9, 2024
431d523
fix type error
maxaleks Jul 10, 2024
3d29521
update envs validator
maxaleks Jul 10, 2024
89fac78
Merge branch 'main' into dapp-rating
maxaleks Jul 10, 2024
4324114
Merge branch 'main' into dapp-rating
maxaleks Jul 10, 2024
99f2306
update popover import
maxaleks Jul 10, 2024
a5e56e3
fix disabled rating behavior
maxaleks Jul 10, 2024
f8e2606
create a new app record if one does not exist
maxaleks Jul 10, 2024
8703e80
show popover instead of tooltip on mobile
maxaleks Jul 10, 2024
638ed38
add margin for popover on mobile
maxaleks Jul 10, 2024
3cd8759
add margin for tooltip
maxaleks Jul 10, 2024
b851dc8
Revert "show popover instead of tooltip on mobile"
maxaleks Jul 10, 2024
4f122b9
disable wallet validation for testing
maxaleks Jul 10, 2024
37c3d34
make controlled tooltip for mobile
maxaleks Jul 10, 2024
9a52446
add margin for rating popover
maxaleks Jul 10, 2024
6d33032
update rating component
maxaleks Jul 11, 2024
201972c
replace favorite icon on banner
maxaleks Jul 11, 2024
c524a3b
change star color for dark theme
maxaleks Jul 11, 2024
5c85b6c
fix popover bug
maxaleks Jul 11, 2024
cb4d8e8
replace favorite icon
maxaleks Jul 12, 2024
9152001
update rating design
maxaleks Jul 12, 2024
a5d43c7
hide rating if feature is disabled
maxaleks Jul 12, 2024
cd37b4b
update screenshots
maxaleks Jul 12, 2024
9f7cb35
update screenshot
maxaleks Jul 12, 2024
d97d03e
update evn
maxaleks Jul 15, 2024
5e9c6a1
return back wallet validation
maxaleks Jul 15, 2024
f713619
Merge branch 'main' into dapp-rating
maxaleks Jul 15, 2024
7aa5546
add secret
maxaleks Jul 15, 2024
64e52b9
update tests
maxaleks Jul 15, 2024
dc28dff
add mixpanel events
maxaleks Jul 15, 2024
490d61b
implement rating change
maxaleks Jul 15, 2024
e5f83ac
disable wallet validation
maxaleks Jul 15, 2024
48a26fd
remove unnecessary style
maxaleks Jul 16, 2024
0643a72
Merge branch 'main' into dapp-rating
maxaleks Jul 16, 2024
4cee52c
update tests
maxaleks Jul 16, 2024
b759749
moved the use of the hook to another place
maxaleks Jul 16, 2024
2776830
dapps lazy loading
maxaleks Jul 16, 2024
dc72550
fix svg icon
maxaleks Jul 16, 2024
3d6ae52
change toast margins
maxaleks Jul 17, 2024
ccc01ed
move csp for marketplace to separate file
maxaleks Jul 17, 2024
0b447d5
rename var
maxaleks Jul 17, 2024
8ef0807
move favorite icon to separate file
maxaleks Jul 17, 2024
2d26d67
remove padding on last element
maxaleks Jul 17, 2024
fbc0ac7
fix rating records duplication
maxaleks Jul 17, 2024
7dc8252
add try-catch
maxaleks Jul 17, 2024
3b2cc76
fix tooltip width
maxaleks Jul 17, 2024
5ea5b18
update screenshots with toast
maxaleks Jul 17, 2024
7f63678
fix style of menu tooltip
maxaleks Jul 17, 2024
654382d
add tests for useRatings hook
maxaleks Jul 17, 2024
5852d19
Merge branch 'main' into dapp-rating
maxaleks Jul 17, 2024
b850b46
Merge branch 'main' into dapp-rating
maxaleks Jul 18, 2024
094152b
Merge branch 'main' into dapp-rating
maxaleks Jul 18, 2024
5621018
update screenshots
maxaleks Jul 18, 2024
0120aff
chore: remove trailing spaces and add newline at end of file
maxaleks Jul 18, 2024
2f42b47
Update Address.pw.tsx_default_degradation-view-1.png
maxaleks Jul 18, 2024
a309ca1
change min number of txs
maxaleks Jul 22, 2024
cef5fca
change texts
maxaleks Jul 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions configs/app/features/marketplace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const securityReportsUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_SEC
const featuredApp = getEnvValue('NEXT_PUBLIC_MARKETPLACE_FEATURED_APP');
const bannerContentUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL');
const bannerLinkUrl = getEnvValue('NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL');
const ratingAirtableApiKey = getEnvValue('NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY');
const ratingAirtableBaseId = getEnvValue('NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID');

const title = 'Marketplace';

Expand All @@ -27,6 +29,7 @@ const config: Feature<(
securityReportsUrl: string | undefined;
featuredApp: string | undefined;
banner: { contentUrl: string; linkUrl: string } | undefined;
rating: { airtableApiKey: string; airtableBaseId: string } | undefined;
}> = (() => {
if (enabled === 'true' && chain.rpcUrl && submitFormUrl) {
const props = {
Expand All @@ -39,6 +42,10 @@ const config: Feature<(
contentUrl: bannerContentUrl,
linkUrl: bannerLinkUrl,
} : undefined,
rating: ratingAirtableApiKey && ratingAirtableBaseId ? {
airtableApiKey: ratingAirtableApiKey,
airtableBaseId: ratingAirtableBaseId,
} : undefined,
};

if (configUrl) {
Expand Down
3 changes: 2 additions & 1 deletion configs/envs/.env.eth
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ NEXT_PUBLIC_MARKETPLACE_FEATURED_APP=gearbox-protocol
NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=appGkvtmKI7fXE4Vs
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
NEXT_PUBLIC_METASUITES_ENABLED=true
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG={'name': 'zerion', 'dapp_id': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview?utm_source=blockscout&utm_medium=address', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}
Expand All @@ -59,4 +60,4 @@ NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s-prod-1.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
16 changes: 16 additions & 0 deletions deploy/tools/envs-validator/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,22 @@ const marketplaceSchema = yup
// eslint-disable-next-line max-len
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL cannot not be used without NEXT_PUBLIC_MARKETPLACE_ENABLED'),
}),
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY: yup
.string()
.when('NEXT_PUBLIC_MARKETPLACE_ENABLED', {
is: true,
then: (schema) => schema,
// eslint-disable-next-line max-len
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY cannot not be used without NEXT_PUBLIC_MARKETPLACE_ENABLED'),
}),
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID: yup
.string()
.when('NEXT_PUBLIC_MARKETPLACE_ENABLED', {
is: true,
then: (schema) => schema,
// eslint-disable-next-line max-len
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID cannot not be used without NEXT_PUBLIC_MARKETPLACE_ENABLED'),
}),
});

const beaconChainSchema = yup
Expand Down
2 changes: 2 additions & 0 deletions deploy/tools/envs-validator/test/.env.marketplace
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://example.com
NEXT_PUBLIC_MARKETPLACE_FEATURED_APP=aave
NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/maxaleks/36f779fd7d74877b57ec7a25a9a3a6c9/raw/746a8a59454c0537235ee44616c4690ce3bbf3c8/banner.html
NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://www.basename.app
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY=test
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=test
1 change: 1 addition & 0 deletions deploy/values/review-l2/values.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,4 @@ frontend:
NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID
FAVICON_GENERATOR_API_KEY: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY
NEXT_PUBLIC_OG_IMAGE_URL: https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/base-mainnet.png
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY: ref+vault://deployment-values/blockscout/dev/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY
1 change: 1 addition & 0 deletions deploy/values/review/values.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,4 @@ frontend:
FAVICON_GENERATOR_API_KEY: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_FAVICON_GENERATOR_API_KEY
NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY
NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN: ref+vault://deployment-values/blockscout/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY: ref+vault://deployment-values/blockscout/dev/common?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY
2 changes: 2 additions & 0 deletions docs/ENVS.md
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ This feature is **always enabled**, but you can configure its behavior by passin
| NEXT_PUBLIC_MARKETPLACE_FEATURED_APP | `string` | ID of the featured application to be displayed on the banner on the Marketplace page | - | - | `uniswap` | v1.29.0+ |
| NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL | `string` | URL of the banner HTML content | - | - | `https://example.com/banner` | v1.29.0+ |
| NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL | `string` | URL of the page the banner leads to | - | - | `https://example.com` | v1.29.0+ |
| NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_API_KEY | `string` | Airtable API key | - | - | - | v1.33.0+ |
| NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID | `string` | Airtable base ID with dapp ratings | - | - | - | v1.33.0+ |

#### Marketplace app configuration properties

Expand Down
3 changes: 3 additions & 0 deletions icons/heart_filled.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions icons/heart_outline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions icons/star_filled.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion icons/star_outline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions lib/hooks/useLazyRenderedList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { useInView } from 'react-intersection-observer';
const STEP = 10;
const MIN_ITEMS_NUM = 50;

export default function useLazyRenderedList(list: Array<unknown>, isEnabled: boolean) {
const [ renderedItemsNum, setRenderedItemsNum ] = React.useState(MIN_ITEMS_NUM);
export default function useLazyRenderedList(list: Array<unknown>, isEnabled: boolean, minItemsNum: number = MIN_ITEMS_NUM) {
const [ renderedItemsNum, setRenderedItemsNum ] = React.useState(minItemsNum);
const { ref, inView } = useInView({
rootMargin: '200px',
triggerOnce: false,
skip: !isEnabled || list.length <= MIN_ITEMS_NUM,
skip: !isEnabled || list.length <= minItemsNum,
});

React.useEffect(() => {
Expand Down
7 changes: 7 additions & 0 deletions lib/mixpanel/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export enum EventTypes {
FILTERS = 'Filters',
BUTTON_CLICK = 'Button click',
PROMO_BANNER = 'Promo banner',
APP_FEEDBACK = 'App feedback',
}

/* eslint-disable @typescript-eslint/indent */
Expand Down Expand Up @@ -135,5 +136,11 @@ Type extends EventTypes.PROMO_BANNER ? {
'Source': 'Marketplace';
'Link': string;
} :
Type extends EventTypes.APP_FEEDBACK ? {
'Action': 'Rating';
'Source': 'Discovery' | 'App modal' | 'App page';
'AppId': string;
'Score': number;
} :
undefined;
/* eslint-enable @typescript-eslint/indent */
12 changes: 12 additions & 0 deletions mocks/apps/ratings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { apps } from './apps';

export const ratings = {
records: [
{
fields: {
appId: apps[0].id,
rating: 4.3,
},
},
],
};
4 changes: 3 additions & 1 deletion mocks/apps/securityReports.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { apps } from './apps';

export const securityReports = [
{
appName: 'token-approval-tracker',
appName: apps[0].id,
doc: 'http://docs.li.fi/smart-contracts/deployments#mainnet',
chainsData: {
'1': {
Expand Down
3 changes: 3 additions & 0 deletions nextjs/csp/policies/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ export function app(): CspDev.DirectiveDescriptor {

// github (spec for api-docs page)
'raw.githubusercontent.com',

// airtable (for dapps ratings)
'api.airtable.com',
maxaleks marked this conversation as resolved.
Show resolved Hide resolved
].filter(Boolean),

'script-src': [
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"@types/papaparse": "^5.3.5",
"@types/react-scroll": "^1.8.4",
"@web3modal/wagmi": "4.2.1",
"airtable": "^0.12.2",
"bignumber.js": "^9.1.0",
"blo": "^1.1.1",
"chakra-react-select": "^4.4.3",
Expand Down
2 changes: 2 additions & 0 deletions public/icons/name.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
| "globe-b"
| "globe"
| "graphQL"
| "heart_filled"
| "heart_outline"
| "info"
| "integration/full"
| "integration/partial"
Expand Down
1 change: 1 addition & 0 deletions theme/components/Tooltip/Tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const baseStyle = defineStyle((props) => {
[$fg.variable]: `colors.${ fg }`,
[$arrowBg.variable]: $bg.reference,
maxWidth: props.maxWidth || props.maxW || 'unset',
marginX: '4px',
};
});

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions types/client/marketplace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@ export type MarketplaceAppOverview = MarketplaceAppPreview & MarketplaceAppSocia
site?: string;
}

export type AppRating = {
recordId: string;
value: number;
}

export type MarketplaceAppWithSecurityReport = MarketplaceAppOverview & {
securityReport?: MarketplaceAppSecurityReport;
rating?: AppRating;
}

export enum MarketplaceCategory {
Expand Down
2 changes: 1 addition & 1 deletion ui/marketplace/AppSecurityReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const AppSecurityReport = ({
className={ className }
/>
</PopoverTrigger>
<PopoverContent w={{ base: '100vw', lg: '328px' }}>
<PopoverContent w={{ base: 'calc(100vw - 24px)', lg: '328px' }} mx={{ base: 3, lg: 0 }}>
<PopoverBody px="26px" py="20px" fontSize="sm">
<Text fontWeight="500" fontSize="xs" mb={ 2 } variant="secondary">Smart contracts info</Text>
<Flex alignItems="center" justifyContent="space-between" py={ 1.5 }>
Expand Down
12 changes: 8 additions & 4 deletions ui/marketplace/Banner/FeaturedApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const FeaturedApp = ({
const categoriesLabel = categories.join(', ');

const backgroundColor = useColorModeValue('purple.50', 'whiteAlpha.100');
const heartFilledColor = useColorModeValue('blue.700', 'gray.400');

const handleInfoClick = useCallback((event: MouseEvent) => {
event.preventDefault();
Expand Down Expand Up @@ -135,10 +136,13 @@ const FeaturedApp = ({
w={ 9 }
h={ 8 }
onClick={ handleFavoriteClick }
icon={ isFavorite ?
<IconSvg name="star_filled" w={ 5 } h={ 5 } color="yellow.400"/> :
<IconSvg name="star_outline" w={ 5 } h={ 5 } color="gray.400"/>
}
icon={ (
<IconSvg
name={ isFavorite ? 'heart_filled' : 'heart_outline' }
color={ isFavorite ? heartFilledColor : 'gray.400' }
boxSize={ 5 }
/>
maxaleks marked this conversation as resolved.
Show resolved Hide resolved
) }
/>
) }
</Flex>
Expand Down
12 changes: 8 additions & 4 deletions ui/marketplace/Banner/FeaturedAppMobile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const FeaturedAppMobile = ({
const categoriesLabel = categories.join(', ');

const logoUrl = useColorModeValue(logo, logoDarkMode || logo);
const heartFilledColor = useColorModeValue('blue.700', 'gray.400');

return (
<LinkBox
Expand Down Expand Up @@ -144,10 +145,13 @@ const FeaturedAppMobile = ({
w={ 9 }
h={ 8 }
onClick={ onFavoriteClick }
icon={ isFavorite ?
<IconSvg name="star_filled" w={ 5 } h={ 5 } color="yellow.400"/> :
<IconSvg name="star_outline" w={ 5 } h={ 5 } color="gray.400"/>
}
icon={ (
<IconSvg
name={ isFavorite ? 'heart_filled' : 'heart_outline' }
color={ isFavorite ? heartFilledColor : 'gray.400' }
boxSize={ 5 }
/>
) }
/>
) }
</Flex>
Expand Down
2 changes: 1 addition & 1 deletion ui/marketplace/EmptySearchResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const EmptySearchResult = ({ favoriteApps, selectedCategoryId }: Props) => (
(selectedCategoryId === MarketplaceCategory.FAVORITES && !favoriteApps.length) ? (
<>
You don{ apos }t have any favorite apps.<br/>
Click on the <IconSvg name="star_outline" w={ 4 } h={ 4 } mb={ -0.5 }/> icon on the app{ apos }s card to add it to Favorites.
Click on the <IconSvg name="heart_outline" boxSize={ 5 } mb={ -1 } color="gray.400"/> icon on the app{ apos }s card to add it to Favorites.
</>
) : (
<>
Expand Down
Loading
Loading