Skip to content

Commit

Permalink
Build recent classifications component of signed-in home page (#6112)
Browse files Browse the repository at this point in the history
* fetch authUser's most recent classifications

* add styling to SubjectCard and RecentSubjects
  • Loading branch information
goplayoutside3 authored Jun 6, 2024
1 parent 7d1511e commit 32f4864
Show file tree
Hide file tree
Showing 13 changed files with 797 additions and 1 deletion.
3 changes: 2 additions & 1 deletion packages/app-root/src/components/HomePageContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useContext } from 'react'
import { Box } from 'grommet'
import { PanoptesAuthContext } from '../contexts'
import { CommunityContainer, DefaultHome } from '@zooniverse/content'
import { UserHome } from '@zooniverse/user'
import { Loader } from '@zooniverse/react-components'

export default function HomePageContainer({
Expand All @@ -20,7 +21,7 @@ export default function HomePageContainer({
</Box>
) : (
<Box height={{ min: '100vh' }}>
{user?.login ? <p>Signed-in</p> : <DefaultHome />}
{user?.login ? <UserHome authUser={user}/> : <DefaultHome />}
</Box>
)}
<CommunityContainer
Expand Down
20 changes: 20 additions & 0 deletions packages/lib-user/src/components/UserHome/UserHome.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { shape, string } from 'prop-types'

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

function UserHome({ authUser }) {
return (
<Layout>
<RecentSubjectsContainer authUser={authUser} />
</Layout>
)
}

export default UserHome

UserHome.propTypes = {
authUser: shape({
id: string
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Anchor, Box, ResponsiveContext, Text } from 'grommet'
import { arrayOf, bool, shape, string } from 'prop-types'
import { useContext } from 'react'
import { Loader, SpacedHeading, SpacedText } from '@zooniverse/react-components'

import SubjectCard from '../SubjectCard/SubjectCard.js'

function RecentSubjects({
isLoading = false,
recents = [],
recentsError = undefined
}) {
const size = useContext(ResponsiveContext)

return (
<Box
pad='medium'
height={{ min: '200px' }}
round='small'
border={{
color: { light: 'light-5', dark: 'black' },
size: 'xsmall'
}}
>
<SpacedHeading size='1.125rem' level={2} margin={{ top: '0' }}>
Recent Classifications
</SpacedHeading>
<Box
as='ul'
direction='row'
gap='small'
pad={{ horizontal: 'xxsmall', bottom: 'xsmall' }}
overflow={{ horizontal: 'auto' }}
style={{ listStyle: 'none' }}
margin='0'
>
{isLoading && (
<Box fill justify='center' align='center'>
<Loader />
</Box>
)}
{!isLoading && recentsError && (
<Box fill justify='center' align='center' pad='medium'>
<SpacedText>
There was an error fetching recent classifications
</SpacedText>
</Box>
)}
{!isLoading && !recents?.length && !recentsError && (
<Box fill justify='center' align='center' pad='medium'>
<SpacedText>No Recent Classifications found</SpacedText>
<Text>
Start by{' '}
<Anchor href='https://www.zooniverse.org/projects'>
classifying any project
</Anchor>{' '}
to show your recent classifications here.
</Text>
</Box>
)}
{!isLoading && recents?.length
? recents.slice(0, 10).map(classification => {
const subjectMedia = classification.locations.map(
location => Object.values(location)[0]
)
return (
<SubjectCard
key={classification.id}
size={size}
subjectID={classification.links.subject}
mediaSrc={subjectMedia[0]}
projectSlug={classification.slug}
/>
)
})
: null}
</Box>
</Box>
)
}

export default RecentSubjects

RecentSubjects.propTypes = {
isLoading: bool,
recents: arrayOf(
shape({
id: string,
slug: string
})
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import RecentSubjects from './RecentSubjects'
import mockRecents from './recents.mock.json'

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

export const Default = {
args: {
recents: mockRecents
}
}

export const NoSubjects = {
args: {
recents: []
}
}

export const Error = {
args: {
recents: [],
recentsError: { message: `Couldn't fetch recent classifications` }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import useSWR from 'swr'
import { shape, string } from 'prop-types'

import fetchRecentSubjects from './fetchRecentSubjects.js'
import RecentSubjects from './RecentSubjects.js'

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

function RecentSubjectsContainer({ authUser }) {
const cacheKey = {
name: 'user-recent-classifications',
userId: authUser.id
}
const {
data: recents,
error: recentsError,
isLoading
} = useSWR(cacheKey, fetchRecentSubjects, SWROptions)

return (
<RecentSubjects isLoading={isLoading} recents={recents} recentsError={recentsError} />
)
}

export default RecentSubjectsContainer

RecentSubjectsContainer.propTypes = {
authUser: shape({
id: string
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { panoptes } from '@zooniverse/panoptes-js'
import auth from 'panoptes-client/lib/auth'

async function fetchUserRecents() {
try {
const user = await auth.checkCurrent()
const token = await auth.checkBearerToken()
const authorization = `Bearer ${token}`
const query = {
page: 1,
sort: '-created_at'
}
const response = await panoptes.get(`/users/${user.id}/recents`, query, {
authorization
})
return response.body?.recents
} catch (error) {
console.error(error)
return []
}
}

/* Anatomy of a classification object */
// [
// {
// id: "258337208",
// links: {
// project: "19072",
// workflow: "22152",
// subject: "80286011"
// },
// ...
// },
// ...
// ]

async function fetchSubjectLinks(classification) {
try {
const response = await panoptes.get('/projects', {
id: classification.links?.project
})
const slug = response.body?.projects[0].slug
classification.slug = slug
} catch {
console.error(error)
}
}

export default async function fetchRecentSubjects() {
const recents = await fetchUserRecents()

if (recents?.length) {
await Promise.allSettled(recents.map(fetchSubjectLinks))
}

return recents
}
Loading

0 comments on commit 32f4864

Please sign in to comment.