Skip to content

Commit

Permalink
feat: create a public profile for collection contributors (#404)
Browse files Browse the repository at this point in the history
* feat: create a public profile for collection contributors

* fix: prefetch published items for member

* feat: add a back button to member profile
  • Loading branch information
LinaYahya authored Dec 1, 2023
1 parent b671fe6 commit 29f2656
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 2 deletions.
63 changes: 63 additions & 0 deletions pages/members/[id].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { GetServerSideProps, InferGetServerSidePropsType } from 'next';
import { ParsedUrlQuery } from 'querystring';

import * as React from 'react';
import { DehydratedState } from 'react-query';

import { Api, DATA_KEYS, configureQueryClient } from '@graasp/query-client';

import Wrapper from '../../src/components/common/Wrapper';
import Member from '../../src/components/pages/Member';
import { QUERY_CLIENT_OPTIONS } from '../../src/config/queryClient';

const MemberPage = ({
dehydratedState,
id,
}: InferGetServerSidePropsType<typeof getServerSideProps>) => {
return (
<Wrapper dehydratedState={dehydratedState}>
<Member id={id} />
</Wrapper>
);
};

type Props = {
dehydratedState: DehydratedState;
id?: string;
};

interface Params extends ParsedUrlQuery {
id: string;
}

// This gets called on every request
export const getServerSideProps: GetServerSideProps<Props, Params> = async ({
params,
}) => {
const id = params?.id;
const { queryClient, dehydrate, axios } =
configureQueryClient(QUERY_CLIENT_OPTIONS);
if (id) {
// prefetch data in query client
await queryClient.prefetchQuery(DATA_KEYS.buildItemKey(id), () =>
Promise.all([
Api.getMember({ id }, { ...QUERY_CLIENT_OPTIONS, axios }).then(
(data) => {
return JSON.parse(JSON.stringify(data));
},
),
Api.getPublishedItemsForMember(id, {
...QUERY_CLIENT_OPTIONS,
axios,
}).then((data) => {
return JSON.parse(JSON.stringify(data));
}),
]),
);
}

// Pass data to the page via props
return { props: { id, dehydratedState: dehydrate(queryClient) } };
};

export default MemberPage;
10 changes: 9 additions & 1 deletion src/components/collection/Authorship.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import dynamic from 'next/dynamic';
import Link from 'next/link';

import React, { useContext } from 'react';

Expand All @@ -9,6 +10,7 @@ import { DiscriminatedItem, PermissionLevel, ThumbnailSize } from '@graasp/sdk';

import { DEFAULT_MEMBER_THUMBNAIL } from '../../config/constants';
import { useLibraryTranslation } from '../../config/i18n';
import { buildMemberRoute } from '../../config/routes';
import { SUMMARY_AUTHOR_CONTAINER_ID } from '../../config/selectors';
import LIBRARY from '../../langs/constants';
import { QueryClientContext } from '../QueryClientContext';
Expand Down Expand Up @@ -63,7 +65,13 @@ const Authorship = ({ itemId, author, displayCoEditors }: Props) => {
variant="circular"
sx={{ maxWidth: 30, maxHeight: 30 }}
/>
<Typography variant="body1">{author?.name}</Typography>
<Typography
component={Link}
href={buildMemberRoute(author.id)}
variant="body1"
>
{author?.name}
</Typography>
</>
)}
</Stack>
Expand Down
7 changes: 6 additions & 1 deletion src/components/collection/Contributors.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import Link from 'next/link';

import AvatarGroup from '@mui/lab/AvatarGroup';
import { Stack, Tooltip, Typography } from '@mui/material';

import { Member } from '@graasp/sdk';

import { useLibraryTranslation } from '../../config/i18n';
import { buildMemberRoute } from '../../config/routes';
import { buildContributorId } from '../../config/selectors';
import LIBRARY from '../../langs/constants';
import MemberAvatar from '../layout/MemberAvatar';
Expand Down Expand Up @@ -34,7 +37,9 @@ const Contributors = ({ contributors, displayContributors }: Props) => {
const { id, name: contributorName } = contributor;
return (
<Tooltip title={contributorName} key={id} arrow>
<MemberAvatar id={buildContributorId(id)} memberId={id} />
<Link href={buildMemberRoute(id)}>
<MemberAvatar id={buildContributorId(id)} memberId={id} />
</Link>
</Tooltip>
);
})}
Expand Down
111 changes: 111 additions & 0 deletions src/components/pages/Member.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import dynamic from 'next/dynamic';

import { useContext } from 'react';

import { Box, Stack, Typography } from '@mui/material';

import { Context } from '@graasp/sdk';

import { APP_AUTHOR } from '../../config/constants';
import { useLibraryTranslation } from '../../config/i18n';
import { ERROR_UNEXPECTED_ERROR_CODE } from '../../config/messages';
import { MENU_BUTTON_ID } from '../../config/selectors';
import LIBRARY from '../../langs/constants';
import { QueryClientContext } from '../QueryClientContext';
import CollectionsGrid from '../collection/CollectionsGrid';
import BackButton from '../common/BackButton';
import Error from '../common/Error';
import Seo from '../common/Seo';
import useHeader from '../layout/useHeader';

interface Props {
id?: string;
}

const Main = dynamic(() => import('@graasp/ui').then((mod) => mod.Main), {
ssr: false,
});

const Avatar = dynamic(() => import('@graasp/ui').then((mod) => mod.Avatar), {
ssr: false,
});

const Member = ({ id }: Props) => {
const { hooks } = useContext(QueryClientContext);
const { data, isLoading: isMemberInfoLoading, isError } = hooks.useMember(id);
const { data: memberPublishedItems, isLoading } =
hooks.usePublishedItemsForMember(id);

const { leftContent, rightContent } = useHeader();
const { t } = useLibraryTranslation();

if (isError) {
return (
<Main
context={Context.Library}
headerLeftContent={leftContent}
headerRightContent={rightContent}
>
<Box id={id} p={5}>
<Error code={ERROR_UNEXPECTED_ERROR_CODE} />
</Box>
</Main>
);
}
return (
<>
<Seo
title={t(LIBRARY.GRAASP_LIBRARY)}
description={t(LIBRARY.GRAASP_LIBRARY_DESCRIPTION)}
author={APP_AUTHOR}
/>
<Main
context={Context.Library}
menuButtonId={MENU_BUTTON_ID}
headerLeftContent={leftContent}
headerRightContent={rightContent}
>
<Stack
maxWidth="xl"
marginX="auto"
alignItems="flex-start"
justifyItems="flex-start"
justifySelf="center"
marginTop={2}
padding={3}
>
<BackButton />

<Box sx={{ display: 'flex', my: 3, gap: 2, alignItems: 'center' }}>
<Avatar
alt={t(LIBRARY.AVATAR_ALT, { name: data?.name })}
isLoading={isMemberInfoLoading}
component="avatar"
maxWidth={120}
maxHeight={120}
variant="circular"
sx={{ width: 90, height: 90, fontSize: '2.5rem' }}
/>
<Box>
<Typography variant="h5">{data?.name}</Typography>
{/* Bio goes there */}
{/* <Typography variant="body1"></Typography> */}
</Box>
</Box>
<Box flexGrow={1}>
<Typography variant="h6" fontWeight="bold" sx={{ mb: 4 }}>
{t(LIBRARY.PUBLISHED_COLLECTIONS)}
</Typography>

<CollectionsGrid
collections={memberPublishedItems}
isLoading={isLoading}
/>
</Box>
</Stack>
</Main>
</>
);
};

export default Member;
2 changes: 2 additions & 0 deletions src/config/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export const HOME_ROUTE = '/';
export const ALL_COLLECTIONS_ROUTE = '/all-collections';
export const MY_LIKED_ITEMS_ROUTE = '/liked';
export const ERROR_ROUTE = '/error';

export const buildMemberRoute = (id = ':id') => `/members/${id}`;
1 change: 1 addition & 0 deletions src/langs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export const LIBRARY = {
SEARCH_NO_RESULTS: 'SEARCH_NO_RESULTS',
SEARCH_RESULTS_LOAD_MORE: 'SEARCH_RESULTS_LOAD_MORE',
CONTENT_CHIP: 'CONTENT_CHIP',
PUBLISHED_COLLECTIONS: 'PUBLISHED_COLLECTIONS',
LIKED_ITEMS: 'LIKED_ITEMS',
SIGNIN_MESSAGE: 'SIGNIN_MESSAGE',
};
Expand Down
1 change: 1 addition & 0 deletions src/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
"SEARCH_NO_RESULTS": "Nothing corresponds to your search",
"SEARCH_RESULTS_LOAD_MORE": "Load more",
"CONTENT_CHIP": "Content",
"PUBLISHED_COLLECTIONS": "Published Collections",
"LIKED_ITEMS": "Liked Items",
"SIGNIN_MESSAGE": "You need to sign in First"
}

0 comments on commit 29f2656

Please sign in to comment.