Skip to content

Commit

Permalink
Merge pull request #820 from ensdomains/fet-1546
Browse files Browse the repository at this point in the history
fet-1546: Empty Profile Banner
  • Loading branch information
sugh01 authored Sep 16, 2024
2 parents 83d3fb3 + c3fc41d commit bcd6140
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 14 deletions.
5 changes: 5 additions & 0 deletions public/locales/en/profile.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
"ownership": "Ownership",
"viewDetails": "View Details",
"banner": {
"empty": {
"title": "Personalize your profile",
"description": "Add crypto addresses, social links, an avatar and more!",
"action": "Get started"
},
"available": {
"title": "{{name}} is available",
"description": "This name expired on <strong>{{date}}</strong>. Click here to view the registration page."
Expand Down
12 changes: 12 additions & 0 deletions src/assets/Stars.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 18 additions & 14 deletions src/components/pages/profile/[name]/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { OG_IMAGE_URL } from '@app/utils/constants'
import { shouldRedirect } from '@app/utils/shouldRedirect'
import { formatFullExpiry, makeEtherscanLink } from '@app/utils/utils'

import { ProfileEmptyBanner } from './ProfileEmptyBanner'
import MoreTab from './tabs/MoreTab/MoreTab'
import { OwnershipTab } from './tabs/OwnershipTab/OwnershipTab'
import { PermissionsTab } from './tabs/PermissionsTab/PermissionsTab'
Expand Down Expand Up @@ -247,20 +248,23 @@ const ProfileContent = ({ isSelf, isLoading: parentIsLoading, name }: Props) =>
info: infoBanner,
warning,
header: (
<TabButtonContainer>
{visibileTabs.map((tabItem) => (
<TabButton
key={tabItem}
data-testid={`${tabItem}-tab`}
$selected={tabItem === tab}
onClick={() => setTab(tabItem)}
>
<Typography fontVariant="extraLargeBold" color="inherit">
{t(`tabs.${tabItem}.name`)}
</Typography>
</TabButton>
))}
</TabButtonContainer>
<>
<TabButtonContainer>
{visibileTabs.map((tabItem) => (
<TabButton
key={tabItem}
data-testid={`${tabItem}-tab`}
$selected={tabItem === tab}
onClick={() => setTab(tabItem)}
>
<Typography fontVariant="extraLargeBold" color="inherit">
{t(`tabs.${tabItem}.name`)}
</Typography>
</TabButton>
))}
</TabButtonContainer>
<ProfileEmptyBanner name={normalisedName} />
</>
),
titleExtra: profile?.address ? (
<Outlink
Expand Down
87 changes: 87 additions & 0 deletions src/components/pages/profile/[name]/ProfileEmptyBanner.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { mockFunction, render, screen } from '@app/test-utils'

import { describe, expect, it, vi } from 'vitest'

import { useProfileActions } from '@app/hooks/pages/profile/[name]/profile/useProfileActions/useProfileActions'
import { useProfile } from '@app/hooks/useProfile'

import { ProfileEmptyBanner } from './ProfileEmptyBanner'

vi.mock('@app/hooks/useProtectedRoute', () => ({
useProtectedRoute: vi.fn(),
}))
vi.mock('@app/utils/BreakpointProvider')
vi.mock('next/router', async () => await vi.importActual('next-router-mock'))
vi.mock('@app/hooks/useProfile')
vi.mock('@app/hooks/pages/profile/[name]/profile/useProfileActions/useProfileActions')

const mockUseProfile = mockFunction(useProfile)
const mockUseProfileActions = mockFunction(useProfileActions)

describe('ProfileEmptyBanner', () => {
it('should not display banner if have records', () => {
const name = 'test'

mockUseProfile.mockImplementation(() => ({
data: {
texts: [
{
key: 'avatar',
value: 'http://localhost:3000',
},
],
coins: [
{
id: 60,
name: 'eth',
value: '0x8327FcD61f5e90e1E05A3F49DCbc9346b7d111111',
},
],
contentHash: null,
abi: null,
resolverAddress: '0x8327FcD61f5e90e1E05A3F49DCbc9346b7d111112',
isMigrated: true,
createdAt: {
date: '2024-08-02T10:33:00.000Z',
value: 1722594780000,
},
address: '0x8327FcD61f5e90e1E05A3F49DCbc9346b7d175f7',
},
isLoading: false,
}))

mockUseProfileActions.mockImplementation(() => ({
profileActions: [
{
label: 'tabs.profile.actions.editProfile.label',
},
],
}))

render(<ProfileEmptyBanner name={name} />)
expect(screen.queryByTestId('profile-empty-banner')).not.toBeInTheDocument()
})

it('should display banner if have no records', () => {
const name = 'test'

mockUseProfile.mockImplementation(() => ({
data: {
text: [],
coins: [],
},
isLoading: false,
}))

mockUseProfileActions.mockImplementation(() => ({
profileActions: [
{
label: 'tabs.profile.actions.editProfile.label',
},
],
}))

render(<ProfileEmptyBanner name={name} />)
expect(screen.queryByTestId('profile-empty-banner')).toBeInTheDocument()
})
})
69 changes: 69 additions & 0 deletions src/components/pages/profile/[name]/ProfileEmptyBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { useTranslation } from 'react-i18next'
import styled, { css } from 'styled-components'

import { Button, mq, Typography } from '@ensdomains/thorin'

import StarsSVG from '@app/assets/Stars.svg'
import { useProfileActions } from '@app/hooks/pages/profile/[name]/profile/useProfileActions/useProfileActions'
import { useProfile } from '@app/hooks/useProfile'

import { profileToProfileRecords } from './registration/steps/Profile/profileRecordUtils'

const Container = styled.div(
({ theme }) => css`
margin-top: ${theme.space['4']};
display: grid;
grid-template-columns: 48px 1fr auto;
align-items: center;
gap: ${theme.space['6']};
padding: ${theme.space['6']};
width: 100%;
border: 4px solid #fff;
border-radius: 16px;
background: linear-gradient(#e7f4ef 100%, #fdf0dd 100%);
${mq.sm.max(css`
grid-template-columns: 1fr;
text-align: center;
gap: ${theme.space['4']};
padding: ${theme.space['4']};
`)}
`,
)

export function ProfileEmptyBanner({ name }: { name: string }) {
const { t } = useTranslation('profile')

const { data: profile, isLoading: isProfileLoading } = useProfile({ name })
const existingRecords = profileToProfileRecords(profile)
const profileActions = useProfileActions({
name,
})

const records = existingRecords.filter(({ value }) => value)

const action = (profileActions.profileActions ?? []).find(
(i) => i.label === t('tabs.profile.actions.editProfile.label'),
)

if (records.length || isProfileLoading || !action) return null

return (
<Container data-testid="profile-empty-banner">
<div>
<StarsSVG />
</div>
<div>
<Typography fontVariant="large" weight="bold" color="textPrimary">
{t('banner.empty.title')}
</Typography>
<Typography color="textPrimary" fontVariant="body">
{t('banner.empty.description')}
</Typography>
</div>
<Button width="auto" colorStyle="orangePrimary" onClick={() => action?.onClick()}>
{t('banner.empty.action')}
</Button>
</Container>
)
}

0 comments on commit bcd6140

Please sign in to comment.