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

View user profiles #582

Merged
merged 11 commits into from
Nov 16, 2024
2 changes: 1 addition & 1 deletion example.env
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ REACT_APP_API_URL=https://fallingfruit.org/api/0.3
REACT_APP_API_KEY=AKDJGHSD
REACT_APP_RECAPTCHA_SITE_KEY=6Ld99kUdAAAAAAB5nCofKrQB6Vp-e5wR42u5TNZZ
REACT_APP_GOOGLE_MAPS_API_KEY=
REACT_APP_GOOGLE_ANALYTICS_TRACKING_ID=
REACT_APP_GOOGLE_ANALYTICS_TRACKING_ID=
2 changes: 2 additions & 0 deletions src/components/desktop/DesktopLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import aboutRoutes from '../about/aboutRoutes'
import authRoutes from '../auth/authRoutes'
import connectRoutes from '../connect/connectRoutes'
import MapPage from '../map/MapPage'
import profileRoutes from '../profile/profileRoutes'
Copy link
Collaborator

Choose a reason for hiding this comment

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

You only included it in the desktop layout - let's also have the user pages on mobile :). If you put the new route in authRoutes (see other comment), it'll work for both layouts.

import Header from './Header'
import SidePane from './SidePane'

Expand Down Expand Up @@ -52,6 +53,7 @@ const DesktopLayout = () => (
<Switch>
{aboutRoutes}
{authRoutes}
{profileRoutes}
<Route>
{connectRoutes}
<WindowSize>
Expand Down
13 changes: 10 additions & 3 deletions src/components/entry/EntryOverview.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,16 @@ const EntryOverview = () => {
<User size={20} />
)}
<p>
{locationData.author && locationData.import_id
? t('imported_from', { name: locationData.author })
: t('added_by', { name: locationData.author })}
{locationData.author && locationData.import_id ? (
t('imported_from', { name: locationData.author })
) : (
<>
{t('added_by', { name: '' })}{' '}
<Link to={`/users/${locationData.user_id}`}>
{locationData.author}
</Link>
</>
)}
{locationData.import_id && (
<>
{locationData.author && ' ('}
Expand Down
12 changes: 11 additions & 1 deletion src/components/entry/Review.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Pencil as PencilIcon } from '@styled-icons/boxicons-solid'
import { Link } from 'react-router-dom'
import styled from 'styled-components/macro'

import { FRUITING_RATINGS, RATINGS } from '../../constants/ratings'
Expand Down Expand Up @@ -146,7 +147,16 @@ const Review = ({
{!editable && (
<cite>
Reviewed on {formatISOString(review.created_at)}
{review.author && <> by {review.author}</>}
{review.author && (
<>
{' by '}
{review.user_id ? (
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not sure how to style this - maybe text-decoration: none (undoing the underline styling) and theme.orange for color?
Screenshot from 2024-11-11 10-24-00

Copy link
Collaborator

Choose a reason for hiding this comment

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

Even undoing all the link styling is an option - then the feature won't be discoverable on mobile for now, and will only be known to be a link on desktop because of pointer cursor, but that's fine, since we don't have much content for the pages yet.

<Link to={`/users/${review.user_id}`}>{review.author}</Link>
) : (
review.author
)}
</>
)}
{review.observed_on && (
<> (visited {formatISOString(review.observed_on)})</>
)}
Expand Down
70 changes: 70 additions & 0 deletions src/components/profile/UserProfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { ArrowBack } from '@styled-icons/boxicons-regular'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useParams } from 'react-router-dom'
import styled from 'styled-components/macro'

import { getUserById } from '../../utils/api'
import { useAppHistory } from '../../utils/useAppHistory'
import { PageScrollWrapper, PageTemplate } from '../about/PageTemplate'
import BackButton from '../ui/BackButton'
import { LoadingOverlay } from '../ui/LoadingIndicator'

const StyledNavBack = styled.div`
svg {
height: 20px;
margin-right: 5px;
}
`
const UserProfile = () => {
const { id } = useParams()
const history = useAppHistory()
const { t } = useTranslation()
const [userData, setUserData] = useState({})
const [isLoading, setIsLoading] = useState(true)

useEffect(() => {
async function fetchUserData() {
setIsLoading(true)
const data = await getUserById(id)
setUserData(data)

// Log the fetched data
console.log('Fetched User Data:', data)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's remove this logging statement when the PR is ready to merge


setIsLoading(false)
}

fetchUserData()
}, [id])

if (isLoading) {
return <LoadingOverlay />
}

const { created_at, name, bio } = userData

return (
<PageScrollWrapper>
<PageTemplate>
<StyledNavBack>
<BackButton
onClick={(event) => {
event.stopPropagation()
history.goBack()
}}
>
<ArrowBack />
{t('back')}
</BackButton>
</StyledNavBack>

<h1>Name: {name}</h1>
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a good start since it shows the data!

Can we iterate on the styling a bit, and try to use fewer words, so there's less need to translate them?

My original quick attempt was a <h2>, user icon for the name, a <p> for a bio, and an italic:

Screenshot from 2024-11-11 10-35-39

Screenshot from 2024-11-11 10-34-32

but I'm not great at styling pages.

Maybe borrow the styling from the import page?
It looks like

<h3> #301: Dumpster Diving locations (Melbs)</h3>

with the rest in <p> elements.

<h3>Joined: {new Date(created_at).toISOString().slice(0, 10)}</h3>
<h3>Bio: {bio}</h3>
</PageTemplate>
</PageScrollWrapper>
)
}

export default UserProfile
16 changes: 16 additions & 0 deletions src/components/profile/profileRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Route } from 'react-router-dom'

import UserProfile from './UserProfile'

const pages = [
{
path: ['/users/:id'],
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of this file, could you put the new route in auth routes, just under /users/sign_in etc. ? It doesn't completely belong there, but it'll help us understand the routing. It needs to be below the more specific routes (otherwise /users/sign_in will try to go to a user profile with id "sign_in", and fail).

component: UserProfile,
},
]

const profileRoutes = pages.map((props) => (
<Route key={props.path[0]} path={props.path} component={props.component} />
))

export default profileRoutes
5 changes: 5 additions & 0 deletions src/utils/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ instance.interceptors.request.use((config) => {
'/clusters',
'/imports',
'/imports/:id',
'/users/:id',
]
const isAnonymous =
config.method === 'get' &&
Expand Down Expand Up @@ -184,3 +185,7 @@ export const getImports = () => instance.get(`/imports`)
export const getImportById = (
id: paths['/imports/{id}']['get']['parameters']['path']['id'],
) => instance.get(`/imports/${id}`)

export const getUserById = (
id: paths['/users/{id}']['get']['parameters']['path']['id'],
) => instance.get(`/users/${id}`)
18 changes: 18 additions & 0 deletions src/utils/apiSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,24 @@ export interface paths {
};
};
};
"/users/{id}": {
get: {
parameters: {
path: {
/** User ID. */
id: components["parameters"]["user_id"];
};
};
responses: {
/** Success */
200: {
content: {
"application/json": components["schemas"]["User"];
};
};
};
};
};
"/locations": {
get: {
parameters: {
Expand Down