Skip to content

Commit

Permalink
Refactor homepage recent projects to use ERAS (#6397)
Browse files Browse the repository at this point in the history
* refactor homepage RecentProjects with useStats()

* correct the ordering in RecentProjectsContainer

* filter out private or deleted projects
  • Loading branch information
goplayoutside3 authored Oct 23, 2024
1 parent d2b2191 commit dd5336e
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 103 deletions.
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
import { Anchor, Box, ResponsiveContext, Text } from 'grommet'
import { arrayOf, bool, shape, string } from 'prop-types'
import { arrayOf, bool, number, shape, string } from 'prop-types'
import { useContext } from 'react'
import { Loader, ProjectCard, SpacedText } from '@zooniverse/react-components'

import { ContentBox } from '@components/shared'

export default function RecentProjects({
isLoading = false,
projectPreferences = [],
recentProjects = [],
error = undefined
}) {
const size = useContext(ResponsiveContext)

return (
<ContentBox title='Continue Classifying' screenSize={size}>
{isLoading && (
{isLoading ? (
<Box fill justify='center' align='center'>
<Loader />
</Box>
)}
{!isLoading && error && (
) : error ? (
<Box fill justify='center' align='center' pad='medium'>
<SpacedText>
There was an error fetching your recent projects
</SpacedText>
</Box>
)}
{!isLoading && !projectPreferences.length && !error && (
) : !recentProjects.length ? (
<Box fill justify='center' align='center' pad='medium'>
<SpacedText>No Recent Projects found</SpacedText>
<Text>
Expand All @@ -37,41 +35,44 @@ export default function RecentProjects({
.
</Text>
</Box>
) : (
<Box
as='ul'
direction='row'
gap='small'
pad={{ horizontal: 'xxsmall', bottom: 'xsmall', top: 'xxsmall' }}
overflow={{ horizontal: 'auto' }}
style={{ listStyle: 'none' }}
margin='0'
>
{recentProjects.map(project => (
<li key={project.id}>
<ProjectCard
badge={project.count}
description={project?.description}
displayName={project?.display_name}
href={`https://www.zooniverse.org/projects/${project?.slug}`}
imageSrc={project?.avatar_src}
size={size}
/>
</li>
))}
</Box>
)}
{!isLoading &&
projectPreferences?.length ? (
<Box
as='ul'
direction='row'
gap='small'
pad={{ horizontal: 'xxsmall', bottom: 'xsmall', top: 'xxsmall' }}
overflow={{ horizontal: 'auto' }}
style={{ listStyle: 'none' }}
margin='0'
>
{projectPreferences.map(preference => (
<li key={preference?.project?.id}>
<ProjectCard
badge={preference?.activity_count}
description={preference?.project?.description}
displayName={preference?.project?.display_name}
href={`https://www.zooniverse.org/projects/${preference?.project?.slug}`}
imageSrc={preference?.project?.avatar_src}
size={size}
/>
</li>
))}
</Box>
) : null}
</ContentBox>
)
}

RecentProjects.propTypes = {
isLoading: bool,
projectPreferences: arrayOf(
recentProjects: arrayOf(
shape({
id: string
avatar_src: string,
count: number,
description: string,
display_name: string,
id: string,
slug: string
})
)
}
Original file line number Diff line number Diff line change
@@ -1,90 +1,56 @@
import { shape, string } from 'prop-types'
import { panoptes } from '@zooniverse/panoptes-js'
import useSWR from 'swr'
import auth from 'panoptes-client/lib/auth'

import { usePanoptesProjects } from '@hooks'
import { usePanoptesProjects, useStats } from '@hooks'
import RecentProjects from './RecentProjects.js'

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

async function fetchUserProjectPreferences() {
const user = await auth.checkCurrent()
const token = await auth.checkBearerToken()
const authorization = `Bearer ${token}`
try {
const query = {
sort: '-updated_at',
user_id: user.id
}
const response = await panoptes.get('/project_preferences', query, { authorization })
if (response.ok) {
const projectPreferencesUserHasClassified =
response.body.project_preferences
.filter(preference => preference.activity_count > 0)
return projectPreferencesUserHasClassified
}
return []
} catch (error) {
console.error(error)
throw error
}
}

function RecentProjectsContainer({ authUser }) {
// Get user's project preference.activity_count for 10 most recently classified projects
const cacheKey = {
name: 'user-project-preferences',
userId: authUser.id
const recentProjectsQuery = {
project_contributions: true,
order_project_contributions_by: 'recents',
period: 'day'
}

const {
data: projectPreferences,
isLoading: preferencesLoading,
error: preferencesError
} = useSWR(cacheKey, fetchUserProjectPreferences, SWROptions)
data: stats,
isLoading: statsLoading,
error: statsError
} = useStats({ sourceId: authUser?.id, query: recentProjectsQuery })

// Get more info about each project and attach it to correct projectPreference object
const recentProjectIds = projectPreferences?.map(
preference => preference.links.project
)
// limit to 20 projects fetched from panoptes
const contributions = stats?.project_contributions.slice(0, 20)
const projectIds = contributions?.map(project => project.project_id)

// Get more info about each project
const {
data: projects,
isLoading: projectsLoading,
error: projectsError
} = usePanoptesProjects({
cards: true,
id: recentProjectIds?.join(',')
id: projectIds?.join(',')
})

// Attach project object to each project preference
let projectPreferencesWithProjectObj
if (projects?.length) {
projectPreferencesWithProjectObj = projectPreferences
.map(preference => {
const matchedProjectObj = projects.find(
project => project.id === preference.links?.project
)

if (matchedProjectObj) {
preference.project = matchedProjectObj
// Attach project info to each contribution stat (see similar behavior in TopProjects)
let recentProjects = []

if (projects?.length && contributions?.length) {
recentProjects = contributions
.map(projectContribution => {
const projectData = projects?.find(
project => project.id === projectContribution.project_id.toString()
)
return {
count: projectContribution.count,
...projectData
}
return preference
})
.filter(preference => preference?.project?.slug)
.slice(0, 10)
.filter(project => project?.id) // exclude private or deleted projects
}

return (
<RecentProjects
isLoading={preferencesLoading || projectsLoading}
projectPreferences={projectPreferencesWithProjectObj}
error={preferencesError || projectsError}
error={statsError || projectsError}
isLoading={statsLoading || projectsLoading}
recentProjects={recentProjects}
/>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { SpacedText } from '@zooniverse/react-components'
import { Anchor } from 'grommet'
import { shape, string } from 'prop-types'
import { object, oneOfType, shape, string } from 'prop-types'
import styled from 'styled-components'

const StyledAnchor = styled(Anchor)`
font-family: 'Karla', Arial, sans-serif;
font-family: 'Karla', Arial, sans-serif;
font-size: 1rem;
line-height: normal;
background: none;
Expand Down Expand Up @@ -45,7 +45,7 @@ function ContentLink({

ContentLink.propTypes = {
link: shape({
as: string,
as: oneOfType([object, string]),
href: string,
text: string
})
Expand Down

0 comments on commit dd5336e

Please sign in to comment.