List other user's tunes and profile, closes: #858

This commit is contained in:
Piotr Rogowski 2022-10-30 16:53:41 +01:00
parent d10dabf04e
commit 328f8e12a1
No known key found for this signature in database
GPG Key ID: 4A842D702D9C6F8F
7 changed files with 127 additions and 6 deletions

View File

@ -63,6 +63,7 @@ const ResetPasswordConfirmation = lazy(() => import('./pages/auth/ResetPasswordC
const EmailVerification = lazy(() => import('./pages/auth/EmailVerification'));
const OauthCallback = lazy(() => import('./pages/auth/OauthCallback'));
const About = lazy(() => import('./pages/About'));
const User = lazy(() => import('./pages/User'));
const { Content } = Layout;
@ -202,6 +203,7 @@ const App = ({ ui, tuneData }: { ui: UIState, tuneData: TuneDataState }) => {
<Route path={Routes.PROFILE} element={<ContentFor element={<Profile />} />} />
<Route path={Routes.RESET_PASSWORD} element={<ContentFor element={<ResetPassword />} />} />
<Route path={Routes.ABOUT} element={<ContentFor element={<About />} />} />
<Route path={Routes.USER_ROOT} element={<ContentFor element={<User />} />} />
<Route path={Routes.EMAIL_VERIFICATION} element={<ContentFor element={<EmailVerification />} />} />
<Route path={Routes.RESET_PASSWORD_CONFIRMATION} element={<ContentFor element={<ResetPasswordConfirmation />} />} />

View File

@ -196,7 +196,8 @@ select:-webkit-autofill:focus {
animation: wiggle 2s linear 1;
}
.ant-table-row {
.ant-table-row,
.ant-list-item {
&.unlisted {
opacity: 0.5;
}

View File

@ -126,6 +126,7 @@ const useDb = () => {
const list = await tunesCollection.getList(page, perPage, {
sort: '-updated',
filter: `author = "${userId}"`,
expand: 'author',
});
return Promise.resolve({

View File

@ -1,3 +1,4 @@
import { Link } from 'react-router-dom';
import {
Button,
Grid,
@ -166,6 +167,11 @@ const Hub = () => {
dataIndex: 'authorUsername',
key: 'authorUsername',
responsive: ['sm'],
render: (userName: string, record: TunesRecordFull) => (
<Link to={generatePath(Routes.USER_ROOT, { userId: record.author })}>
{userName}
</Link>
),
},
{
title: 'Signature',
@ -188,7 +194,7 @@ const Hub = () => {
{
dataIndex: 'tuneId',
fixed: 'right',
render: (tuneId: string, record) => {
render: (tuneId: string, record: TunesRecordFull) => {
const isOwner = currentUser?.id === record.author;
const size = isOwner ? 'small' : 'middle';

113
src/pages/User.tsx Normal file
View File

@ -0,0 +1,113 @@
import {
useEffect,
useState,
} from 'react';
import {
generatePath,
useMatch,
useNavigate,
} from 'react-router-dom';
import {
Button,
Divider,
List,
Pagination,
Typography,
} from 'antd';
import { ArrowRightOutlined } from '@ant-design/icons';
import { Routes } from '../routes';
import { formatTime } from '../utils/time';
import useDb from '../hooks/useDb';
import { aspirationMapper } from '../utils/tune/mappers';
import {
TunesRecordFull,
UsersRecordFull,
} from '../types/dbData';
const tunePath = (tuneId: string) => generatePath(Routes.TUNE_TUNE, { tuneId });
const Profile = () => {
const navigate = useNavigate();
const route = useMatch(Routes.USER_ROOT);
const { getUserTunes } = useDb();
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [total, setTotal] = useState(0);
const [isTunesLoading, setIsTunesLoading] = useState(false);
const [tunesDataSource, setTunesDataSource] = useState<TunesRecordFull[]>([]);
const [username, setUsername] = useState();
const loadData = async () => {
setIsTunesLoading(true);
try {
const { items, totalItems } = await getUserTunes(route?.params.userId!, page, pageSize);
setTotal(totalItems);
setUsername((items[0].expand.author as UsersRecordFull).username);
const mapped = items.map((tune) => ({
...tune,
key: tune.tuneId,
year: tune.year,
displacement: `${tune.displacement}l`,
aspiration: aspirationMapper[tune.aspiration],
published: formatTime(tune.updated),
}));
setTunesDataSource(mapped as any);
} catch (error) {
// request cancelled
} finally {
setIsTunesLoading(false);
}
};
useEffect(() => {
loadData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [page]);
return (
<div className="small-container">
<Divider>{username ? `${username}'s tunes` : 'No tunes yet'}</Divider>
<List
dataSource={tunesDataSource}
loading={isTunesLoading}
renderItem={(tune) => (
<List.Item
actions={[
<Button icon={<ArrowRightOutlined />} onClick={() => navigate(tunePath(tune.tuneId))} />,
]}
className={tune.visibility}
>
<List.Item.Meta
title={<>
{tune.vehicleName} <Typography.Text code>{tune.signature}</Typography.Text>
</>}
description={<>
{tune.engineMake}, {tune.engineCode}, {tune.displacement}, {tune.aspiration}
</>}
/>
<div>
<Typography.Text italic>{tune.published}</Typography.Text>
</div>
</List.Item>
)}
footer={
<div style={{ textAlign: 'right' }}>
<Pagination
style={{ marginTop: 10 }}
pageSize={pageSize}
current={page}
total={total}
onChange={(newPage, newPageSize) => {
setIsTunesLoading(true);
setPage(newPage);
setPageSize(newPageSize);
}}
/>
</div>
}
/>
</div>
);
};
export default Profile;

View File

@ -217,10 +217,7 @@ const Profile = () => {
{tune.vehicleName} <Typography.Text code>{tune.signature}</Typography.Text>
</>}
description={<>
{tune.engineMake},
{tune.engineCode},
{tune.displacement},
{tune.aspiration}
{tune.engineMake}, {tune.engineCode}, {tune.displacement}, {tune.aspiration}
</>}
/>
<div>

View File

@ -26,6 +26,7 @@ export enum Routes {
OAUTH_CALLBACK = '/auth/oauth-callback/:provider',
ABOUT = '/about',
USER_ROOT = '/user/:userId',
REDIRECT_PAGE_OAUTH_CALLBACK = 'oauth',
}