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

Homepage: Build avatar and profile banner into user dashboard #6117

Merged
merged 8 commits into from
Jun 7, 2024
2 changes: 1 addition & 1 deletion packages/app-root/src/components/HomePageContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function HomePageContainer({
</Box>
) : (
<Box height={{ min: '100vh' }}>
{user?.login ? <UserHome authUser={user}/> : <DefaultHome />}
{user?.login ? <UserHome authUser={user} /> : <DefaultHome />}
</Box>
)}
<CommunityContainer
Expand Down
10 changes: 6 additions & 4 deletions packages/lib-user/dev/components/App/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Contributors,
GroupStats,
MyGroups,
UserHome,
UserStats
} from '@components'

Expand All @@ -31,7 +32,7 @@ function App({
useEffect(() => {
async function checkUserSession() {
setLoading(true)

try {
const user = await auth.checkCurrent()
setUser(user)
Expand All @@ -41,7 +42,7 @@ function App({
setLoading(false)
}
}

auth.listen('change', checkUserSession)

return function () {
Expand Down Expand Up @@ -93,13 +94,14 @@ function App({
</li>
</ul>
</ul>
{user?.id ? <UserHome authUser={user} /> : null}
</div>
)

if (groups) {
const subpaths = groups.split('/')
const groupId = subpaths[0] || ''

if (subpaths[0] === '[user_group_id]') {
content = <p>In the url query param <code>?groups=</code>, please replace <code>[user_group_id]</code> with a user group id.</p>
} else if (subpaths[1] === 'contributors') {
Expand Down Expand Up @@ -185,7 +187,7 @@ function App({
/>
</div>
</header>
{loading ?
{loading ?
<p>Loading...</p> : (
<div>
{content}
Expand Down
2 changes: 2 additions & 0 deletions packages/lib-user/src/components/UserHome/UserHome.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { shape, string } from 'prop-types'

import { Layout } from '@components/shared'
import DashboardContainer from './components/Dashboard/DashboardContainer.js'
import RecentSubjectsContainer from './components/RecentSubjects/RecentSubjectsContainer.js'

function UserHome({ authUser }) {
return (
<Layout>
<DashboardContainer authUser={authUser}/>
<RecentSubjectsContainer authUser={authUser} />
</Layout>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Box, Image, ResponsiveContext } from 'grommet'
import styled from 'styled-components'
import { bool, shape, string } from 'prop-types'
import { useContext } from 'react'

const Relative = styled(Box)`
position: relative;
`

const StyledAvatar = styled(Image)`
width: 128px;
height: 128px;
mcbouslog marked this conversation as resolved.
Show resolved Hide resolved
object-fit: cover;
border-radius: 50%;
border: solid white 6px;
position: absolute;
top: 203px;

// For Grommet breakpoint small
@media (width < 769px) {
width: 80px;
height: 80px;
top: 137px;
}
`

export default function Dashboard({ isLoading = false, user }) {
const size = useContext(ResponsiveContext)

return (
<Relative
fill
align='center'
height={
size !== 'small'
? { min: '270px', max: '270px' }
: { min: '180px', max: '180px' }
}
background={
isLoading || !user?.profile_header
? 'brand'
: { image: `url(${user.profile_header})` }
}
round={size !== 'small' ? { size: '16px', corner: 'top' } : false}
>
<StyledAvatar
alt='User avatar'
src={
!user?.avatar_src || isLoading
? 'https://www.zooniverse.org/assets/simple-avatar.png'
: user.avatar_src
}
/>
</Relative>
)
}

Dashboard.propTypes = {
isLoading: bool,
user: shape({
avatar_src: string,
id: string.isRequired,
profile_header: string
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Dashboard from './Dashboard.js'

export default {
title: 'Components / UserHome / Dashboard',
component: Dashboard
}

const USER = {
admin: false,
avatar_src: 'https://panoptes-uploads-staging.zooniverse.org/user_avatar/e638f5a3-7ffb-4d23-bb08-f296377a2e74.jpeg',
display_name: 'Test User 1',
id: '12345',
login: 'TestUser',
profile_header: 'https://panoptes-uploads.zooniverse.org/user_profile_header/9da9fd16-46c1-4d84-a272-83ac19fb32c3.jpeg'
}

const USER_NO_IMAGES = {
admin: false,
avatar_src: '',
display_name: 'Zootester 123',
id: '847637',
login: 'zootester123',
profile_header: ''
}

export const Default = {
args: {
user: USER
}
}

export const NoAvatarOrBanner = {
args: {
user: USER_NO_IMAGES
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { panoptes } from '@zooniverse/panoptes-js'
import useSWR from 'swr'

import Dashboard from './Dashboard'

const SWROptions = {
revalidateIfStale: true,
revalidateOnMount: true,
revalidateOnFocus: true,
revalidateOnReconnect: true,
refreshInterval: 0
}

/* This is a similar pattern to usePanoptesUser hook, but includes the profile_header */
const fetchProfileBanner = async ({ authUser}) => {
try {
const { body } = await panoptes.get(`/users/${authUser.id}/?include=profile_header`)
const user = body.users?.[0]

if (body.linked?.profile_headers?.length) {
user.profile_header = body.linked.profile_headers[0].src
}
return user
} catch (error) {
console.error(error)
return null
}
}

export default function DashboardContainer({ authUser }) {
const key = { endpoint: '/users/[id]', authUser }
const { data: user, isLoading } = useSWR(key, fetchProfileBanner, SWROptions)

return <Dashboard user={user} isLoading={isLoading} />
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import styled from 'styled-components'
import { Anchor, Box } from 'grommet'
import { Media, SpacedText } from '@zooniverse/react-components'
import { number, string } from 'prop-types'
import { string } from 'prop-types'

const StyledAnchor = styled(Anchor)`
text-decoration: none;
Expand Down Expand Up @@ -99,5 +99,5 @@ SubjectCard.propTypes = {
size: string,
projectSlug: string,
mediaSrc: string,
subjectID: number.isRequired
subjectID: string.isRequired
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor fix for prop type in SubjectCard

}
2 changes: 1 addition & 1 deletion packages/lib-user/src/components/UserHome/index.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { default } from './UserHome'
export { default } from './UserHome.js'