Code Snippets - xMember

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);