Steve Smith
Performer Controller Samples:
import {
Controller,
Injectable,
HttpCode,
HttpStatus,
UsePipes,
ValidationPipe,
Get,
Param,
Query,
HttpException
} from '@nestjs/common';
import {
DataResponse,
PageableData
} from 'src/kernel';
import { PERFORMER_STATUSES } from '../constants';
import {
PerformerDto,
IPerformerResponse
} from '../dtos';
import {
PerformerSearchPayload
} from '../payloads';
import { PerformerService, PerformerSearchService } from '../services';
@Injectable()
@Controller('performers')
export class PerformerController {
constructor(
private readonly performerService: PerformerService,
private readonly performerSearchService: PerformerSearchService
) {}
@Get('/search')
@HttpCode(HttpStatus.OK)
@UsePipes(new ValidationPipe({ transform: true }))
async usearch(
@Query() req: PerformerSearchPayload
): Promise<DataResponse<PageableData<IPerformerResponse>>> {
const data = await this.performerSearchService.search(req);
return DataResponse.ok(data);
}
@Get('/:username')
@HttpCode(HttpStatus.OK)
async getDetails(
@Param('username') performerUsername: string
): Promise<DataResponse<Partial<PerformerDto>>> {
const data = await this.performerService.findByUsername(
performerUsername
);
if (!data || data.status !== PERFORMER_STATUSES.ACTIVE) {
throw new HttpException('This account is suspended', 403);
}
return DataResponse.ok(data.toPublicDetailsResponse());
}
}
Performer View Code Samples
import { PureComponent } from 'react';
import {
Layout, Tabs, Image
} from 'antd';
import { connect } from 'react-redux';
import {
getVideos, moreVideo
} from '@redux/video/actions';
import { getGalleries, moreGalleries } from '@redux/gallery/actions';
import { performerService, utilsService } from 'src/services';
import Head from 'next/head';
import Error from 'next/error';
import {
VideoCameraOutlined, PictureOutlined
} from '@ant-design/icons';
import { ScrollListVideo } from '@components/video';
import { PerformerInfo } from '@components/performer';
import { ScrollListGallery } from '@components/gallery/scroll-list-item';
import {
IPerformer, IUser, IUIConfig, ICountry, IError
} from 'src/interfaces';
import '@components/performer/performer.less';
interface IProps {
error: IError;
ui: IUIConfig;
countries: ICountry[];
currentUser: IUser;
performer: IPerformer;
getVideos: Function;
moreVideo: Function;
getGalleries: Function;
moreGalleries: Function;
videoState: any;
galleryState: any;
}
const { TabPane } = Tabs;
class PerformerProfile extends PureComponent<IProps> {
static authenticate = true;
static noredirect = true;
static async getInitialProps({ ctx }) {
const { query } = ctx;
try {
const [performer, countries] = await Promise.all([
performerService.findOne(query.id, {
Authorization: ctx.token || ''
}),
utilsService.countriesList()
]);
return {
performer: performer?.data,
countries: countries?.data || []
};
} catch (e) {
const error = await Promise.resolve(e);
return { error };
}
}
state = {
itemPerPage: 24,
videoPage: 0,
galleryPage: 0,
activeTab: 'video'
};
async componentDidMount() {
this.loadItems();
}
loadItems = () => {
const { itemPerPage, activeTab } = this.state;
const {
performer,
getGalleries: getGalleriesHandler,
getVideos: getVideosHandler
} = this.props;
switch (activeTab) {
case 'video':
this.setState({ videoPage: 0 }, () => {
getVideosHandler({
limit: itemPerPage,
offset: 0,
performerId: performer._id
});
});
break;
case 'gallery':
this.setState({ galleryPage: 0 }, () => {
getGalleriesHandler({
limit: itemPerPage,
offset: 0,
performerId: performer._id
});
});
break;
default: break;
}
}
loadMoreItem = async () => {
const {
moreVideo: moreVideoHandler,
videoState,
moreGalleries: moreGalleryHandler,
galleryState,
performer
} = this.props;
const {
videoPage, itemPerPage, galleryPage, activeTab
} = this.state;
if (activeTab === 'video') {
if (videoState.items.length >= videoState.total) {
return;
}
this.setState({
videoPage: videoPage + 1
}, () => moreVideoHandler({
limit: itemPerPage,
offset: (videoPage + 1) * itemPerPage,
performerId: performer._id
}));
}
if (activeTab === 'gallery') {
if (galleryState.items.length >= galleryState.total) {
return;
}
this.setState({
videoPage: videoPage + 1
}, () => moreGalleryHandler({
limit: itemPerPage,
offset: (galleryPage + 1) * itemPerPage,
performerId: performer._id
}));
}
};
render() {
const {
error, performer, ui, videoState, galleryState, countries
} = this.props;
if (error) {
return <Error statusCode={error?.statusCode || 404} title={error?.message || 'Profile was not found'} />;
}
const { items: videos = [], total: totalVideos = 0, requesting: loadingVid = true } = videoState;
const { items: galleries = [], total: totalGalleries = 0, requesting: loadingGallery = true } = galleryState;
const {
activeTab
} = this.state;
return (
<Layout>
<Head>
<title>
{`${ui?.siteName} | ${performer?.username}`}
</title>
<meta
name="keywords"
content={`${performer?.username}, ${performer?.name}`}
/>
<meta name="description" content={performer?.bio} />
{/* OG tags */}
<meta
property="og:title"
content={`${ui?.siteName} | ${performer?.username}`}
key="title"
/>
<meta property="og:image" content={performer?.avatar || ui?.logoUrl} />
<meta
property="og:keywords"
content={`${performer?.username}, ${performer?.name}`}
/>
<meta
property="og:description"
content={performer?.bio}
/>
{/* Twitter tags */}
<meta
name="twitter:title"
content={`${ui?.siteName} | ${performer?.name || performer?.username || ''}`}
/>
<meta name="twitter:image" content={performer?.avatar || ui?.logoUrl} />
<meta
name="twitter:description"
content={performer?.bio}
/>
</Head>
<div className="main-container">
<div className="top-profile" style={{ backgroundImage: `url(${performer?.cover || '/banner-image.jpg'})` }}>
<div className="bg-top">
<div className="top-left">
<Image src={performer?.avatar || '/no-avatar.png'} fallback="/no-avatar.png" />
<div className="m-name">
<h2>
{performer?.name || 'N/A'}
</h2>
<h5>
@
{performer?.username || 'n/a'}
</h5>
</div>
</div>
</div>
</div>
<div className="pro-desc">
<PerformerInfo countries={countries || []} performer={performer && performer} />
</div>
<div className="model-content">
<Tabs
defaultActiveKey="video"
activeKey={activeTab}
size="large"
onTabClick={(tab) => {
this.setState({ activeTab: tab }, () => this.loadItems());
}}
>
<TabPane
tab={(
<span>
<VideoCameraOutlined />
VIDEOS
</span>
)}
key="video"
>
<ScrollListVideo
items={videos}
loading={loadingVid}
canLoadmore={videos && videos.length < totalVideos}
loadMore={this.loadMoreItem.bind(this)}
currencySymbol={ui.currencySymbol}
/>
</TabPane>
<TabPane
tab={(
<span>
<PictureOutlined />
GALLERIES
</span>
)}
key="gallery"
>
<ScrollListGallery
items={galleries}
loading={loadingGallery}
canLoadmore={galleries && galleries.length < totalGalleries}
loadMore={this.loadMoreItem.bind(this)}
currencySymbol={ui.currencySymbol}
/>
</TabPane>
</Tabs>
</div>
</div>
</Layout>
);
}
}
const mapStates = (state: any) => ({
ui: state.ui,
videoState: { ...state.video.videos },
galleryState: { ...state.gallery.galleries },
currentUser: { ...state.user.current }
});
const mapDispatch = {
getVideos,
moreVideo,
getGalleries,
moreGalleries
};
export default connect(mapStates, mapDispatch)(PerformerProfile);
{"serverDuration": 39, "requestCorrelationId": "42b8a43f031242beb8c44a7ed799c7c2"}