Implemented UserPage

This commit is contained in:
2023-07-19 23:25:25 +03:00
parent 7cf83d099d
commit 7a044970f0
9 changed files with 272 additions and 13 deletions

View File

@ -0,0 +1,25 @@
import { Link } from 'react-router-dom'
import { Navbar } from 'react-bootstrap'
import BackButton from '../assets/backArrow.svg'
type BackHeaderProps = {
text: string
}
function BackHeader({ text }: BackHeaderProps) {
return (
<Navbar>
<Navbar.Brand as={Link} to='/'>
<img src={BackButton} alt='Go back' />
</Navbar.Brand>
<Navbar.Text className='me-auto'>
<h4 className='mb-0'>
{text}
</h4>
</Navbar.Text>
</Navbar>
)
}
export default BackHeader

View File

@ -0,0 +1,25 @@
import { StoriesPreview } from '.'
import { UserCategoriesNames, UserCategory, composeUserCategoriesFilters } from '../assets/userCategories'
import { useAnnouncements } from '../hooks/api'
import { gotError } from '../hooks/useFetch'
type CategoryPreviewProps = {
category: UserCategory
}
function CategoryPreview({ category }: CategoryPreviewProps) {
const announcements = useAnnouncements(composeUserCategoriesFilters[category]())
return (
<section>
<h4 className='fw-bold'>{UserCategoriesNames[category]}</h4>
{gotError(announcements) ? (
<p className='text-danger'>{announcements.error}</p>
) : (announcements.loading ? 'Загрузка...' :
<StoriesPreview announcements={announcements.data} category={category} />
)}
</section>
)
}
export default CategoryPreview

View File

@ -0,0 +1,143 @@
import { Link } from 'react-router-dom'
import { CSSProperties, useEffect, useMemo, useRef, useState } from 'react'
import { UserCategory, userCategoriesInfos } from '../assets/userCategories'
import { Announcement } from '../api/announcement/types'
import { categoryGraphics, categoryNames } from '../assets/category'
import rightAngleIcon from '../assets/rightAngle.svg'
import { Button } from 'react-bootstrap'
type StoriesPreviewProps = {
announcements: Announcement[],
category: UserCategory,
}
const styles = {
container: {
transform: 'translateX(0)',
} as CSSProperties,
ul: {
display: 'flex',
gap: 10,
listStyleType: 'none',
overflow: 'scroll',
paddingLeft: 0,
scrollBehavior: 'smooth',
} as CSSProperties,
link: {
textDecoration: 'none',
color: 'var(--bs-body-color)'
} as CSSProperties,
image: {
height: '25vh',
maxWidth: 'calc(25vh * 9 / 16)',
objectFit: 'contain',
borderRadius: 12,
marginBottom: 5,
} as CSSProperties,
title: {
overflow: 'hidden',
textOverflow: 'ellipsis',
marginBottom: 5,
} as CSSProperties,
scrollButton: {
position: 'fixed',
right: 0,
top: 0,
zIndex: 100,
background: 'linear-gradient(to right, rgba(17, 17, 17, 0) 0%, rgba(17, 17, 17, 255) 100%)',
display: 'block',
height: '100%',
width: '10%',
border: 'none',
cursor: 'default',
borderRadius: 0,
} as CSSProperties,
leftScrollButton: {
left: 0,
transform: 'scaleX(-1)'
} as CSSProperties,
rightScrollButton: {
right: 0,
} as CSSProperties,
}
function StoriesPreview({ announcements, category }: StoriesPreviewProps) {
const ulElement = useRef<HTMLUListElement | null>(null)
const [showScrollButtons, setShowScrollButtons] = useState({ left: false, right: false })
const determineShowScrollButtons = (ul: HTMLUListElement) => (
setShowScrollButtons({
left: ul.scrollLeft > 0,
right: ul.scrollLeft < (ul.scrollWidth - ul.clientWidth),
})
)
useEffect(() => {
const ul = ulElement.current
if (ul) {
determineShowScrollButtons(ul)
const f = () => determineShowScrollButtons(ul)
ul.addEventListener('scroll', f)
return () => ul.removeEventListener('scroll', f)
}
}, [])
useEffect(() => {
const ul = ulElement.current
if (ul) {
determineShowScrollButtons(ul)
}
}, [announcements])
const doScroll = (forward: boolean) => () => {
const ul = ulElement.current
if (ul) {
const storyWidth = window.innerHeight * 0.25 * 9 / 16 + 10
ul.scrollLeft += forward ? storyWidth : -storyWidth
}
}
return <div style={styles.container}>
{showScrollButtons.left &&
<Button onClick={doScroll(false)} style={{ ...styles.scrollButton, ...styles.leftScrollButton }}>
<img src={rightAngleIcon} alt='Показать ещё' />
</Button>
}
<ul style={styles.ul} className='StoriesPreview_ul' ref={ulElement}>
{useMemo(() => announcements.map((ann, i) => (
<li key={`${category}${i}`}>
<Link to={'/?' + new URLSearchParams({
userId: Number(-1).toString(),
userCat: category,
storyIndex: i.toString()
}).toString()} style={styles.link}>
{ann.src?.endsWith('mp4') ? (
<video src={ann.src} style={styles.image} />
) : (
<img
src={ann.src || categoryGraphics[ann.category]}
alt={'Изображение' + (ann.src ? 'предмета' : categoryNames[ann.category])}
style={styles.image}
/>
)}
<p style={styles.title}>{ann.name}</p>
<p style={styles.title}>{userCategoriesInfos[category](ann)}</p>
</Link>
</li>
)), [announcements, category])}
</ul>
{showScrollButtons.right &&
<Button onClick={doScroll(true)} style={{ ...styles.scrollButton, ...styles.rightScrollButton }}>
<img src={rightAngleIcon} alt='Показать ещё' />
</Button>
}
</div>
}
export default StoriesPreview

View File

@ -7,3 +7,6 @@ export { default as TrashboxMarkers } from './TrashboxMarkers'
export { default as WithToken } from './WithToken'
export { default as ClickHandler } from './ClickHandler'
export { default as AuthForm } from './AuthForm'
export { default as BackHeader } from './BackHeader'
export { default as CategoryPreview } from './CategoryPreview'
export { default as StoriesPreview } from './StoriesPreview'