Issue #16: Grid en search y recomended
This commit is contained in:
15
src/components/Grid.jsx
Normal file
15
src/components/Grid.jsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import './Grid.scss';
|
||||||
|
|
||||||
|
export const Col = (props) => {
|
||||||
|
return <div className="col">{props.children}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Row = (props) => {
|
||||||
|
return <div className="row">{props.children}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Grid = (props) => {
|
||||||
|
return <div className="grid">{props.children}</div>
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
.grid {
|
.grid {
|
||||||
--gutter: 1em;
|
--gutter: 1rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
.searchbar {
|
.searchbar {
|
||||||
margin-bottom: 2em;
|
|
||||||
margin-top: 1em;
|
|
||||||
|
|
||||||
input {
|
input {
|
||||||
border: var(--line-width) solid var(--accent);
|
border: var(--line-width) solid var(--accent);
|
||||||
border-right: none;
|
border-right: none;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import ReactDOM from 'react-dom';
|
|||||||
|
|
||||||
import './styles/reset.css';
|
import './styles/reset.css';
|
||||||
import './styles/main.scss';
|
import './styles/main.scss';
|
||||||
import './styles/grid.scss';
|
|
||||||
import './styles/tabs.scss';
|
import './styles/tabs.scss';
|
||||||
|
|
||||||
import {Nav} from "./components/Nav";
|
import {Nav} from "./components/Nav";
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ ul.tabs {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
border-bottom: var(--line-width) var(--gray-1) solid;
|
border-bottom: var(--line-width) var(--gray-1) solid;
|
||||||
margin: 1rem 0;
|
margin-top: 1em;
|
||||||
|
|
||||||
li.tab {
|
li.tab {
|
||||||
padding: .5rem 1em;
|
padding: .5em 1em;
|
||||||
margin-bottom: -2px;
|
margin-bottom: -2px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {getArtist, getDisc, getSong} from '../services/entity_service';
|
|||||||
|
|
||||||
import {EntityList} from '../components/EntityList';
|
import {EntityList} from '../components/EntityList';
|
||||||
import {CoverArt} from '../components/CoverArt';
|
import {CoverArt} from '../components/CoverArt';
|
||||||
|
import {Grid, Row, Col} from '../components/Grid';
|
||||||
|
|
||||||
const PopularArtists = () => {
|
const PopularArtists = () => {
|
||||||
const artistIDs = ['fa3b825f-7c85-4377-b393-d28a2016e293', 'b2d122f9-eadb-4930-a196-8f221eeb0c66',
|
const artistIDs = ['fa3b825f-7c85-4377-b393-d28a2016e293', 'b2d122f9-eadb-4930-a196-8f221eeb0c66',
|
||||||
@@ -91,23 +92,23 @@ export const Recomended = () => {
|
|||||||
// TODO crear una forma de obtener cosas populares
|
// TODO crear una forma de obtener cosas populares
|
||||||
// Esto es un por mientras hago todo el resto y la pagina de inicio no se vea tan vacia
|
// Esto es un por mientras hago todo el resto y la pagina de inicio no se vea tan vacia
|
||||||
return (
|
return (
|
||||||
<div class='grid'>
|
<Grid>
|
||||||
<div class='row'>
|
<Row>
|
||||||
<div class='col'>
|
<Col>
|
||||||
<h3>Artistas Populares</h3>
|
<h3>Artistas Populares</h3>
|
||||||
<PopularArtists/>
|
<PopularArtists/>
|
||||||
</div>
|
</Col>
|
||||||
<div class='col'>
|
<Col>
|
||||||
<h3>Canciones Populares</h3>
|
<h3>Canciones Populares</h3>
|
||||||
<PopularSongs/>
|
<PopularSongs/>
|
||||||
</div>
|
</Col>
|
||||||
</div>
|
</Row>
|
||||||
<div className="row">
|
<Row>
|
||||||
<div className="col">
|
<Col>
|
||||||
<h3>Discos Populares</h3>
|
<h3>Discos Populares</h3>
|
||||||
<PopularDiscs/>
|
<PopularDiscs/>
|
||||||
</div>
|
</Col>
|
||||||
</div>
|
</Row>
|
||||||
</div>
|
</Grid>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,269 +8,286 @@ import {SearchBar} from "../components/SearchBar";
|
|||||||
import {Paginate} from "../components/Paginate";
|
import {Paginate} from "../components/Paginate";
|
||||||
import {CoverArt} from "../components/CoverArt";
|
import {CoverArt} from "../components/CoverArt";
|
||||||
import {EntityList} from "../components/EntityList";
|
import {EntityList} from "../components/EntityList";
|
||||||
|
import {Grid, Col, Row} from '../components/Grid';
|
||||||
|
|
||||||
|
|
||||||
const SearchSongs = (props) => {
|
const SearchSongs = (props) => {
|
||||||
const [songs, setSongs] = useState(null);
|
const [songs, setSongs] = useState(null);
|
||||||
const [paginate, setPaginate] = useState(null);
|
const [paginate, setPaginate] = useState(null);
|
||||||
const [page, setPage] = useState(props.page ? props.page : 1);
|
const [page, setPage] = useState(props.page ? props.page : 1);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSongs(null);
|
setSongs(null);
|
||||||
setPaginate(null);
|
setPaginate(null);
|
||||||
loadSongs(props.query, page)
|
loadSongs(props.query, page)
|
||||||
}, [props.query, page]);
|
}, [props.query, page]);
|
||||||
|
|
||||||
const makeLink = page => `/search/song?query=${props.query}&page=${page}`;
|
const makeLink = page => `/search/song?query=${props.query}&page=${page}`;
|
||||||
|
|
||||||
const handlePageChange = page => {
|
const handlePageChange = page => {
|
||||||
setSongs(null);
|
setSongs(null);
|
||||||
setPage(page);
|
setPage(page);
|
||||||
props.onPageChange(page);
|
props.onPageChange(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadSongs = (query, page) => {
|
const loadSongs = (query, page) => {
|
||||||
page = page ? page : 1;
|
page = page ? page : 1;
|
||||||
|
|
||||||
searchSong(query, page).then((response) => {
|
searchSong(query, page).then((response) => {
|
||||||
setSongs(response.recordings);
|
setSongs(response.recordings);
|
||||||
setPaginate(response.paginate);
|
setPaginate(response.paginate);
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let songsComponent = <EntityList placeholder={true} size={10}/>;
|
let songsComponent = <EntityList placeholder={true} size={10}/>;
|
||||||
if (songs) {
|
if (songs) {
|
||||||
const items = songs.map((song) => {
|
const items = songs.map((song) => {
|
||||||
return {
|
return {
|
||||||
'cover': null,
|
'cover': null,
|
||||||
'link': `/song/${song.id}`,
|
'link': `/song/${song.id}`,
|
||||||
'title': song.title,
|
'title': song.title,
|
||||||
'subtitle': song.artist.name
|
'subtitle': song.artist.name
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const list = [{
|
const list = [{
|
||||||
'items': items
|
'items': items
|
||||||
}]
|
}]
|
||||||
songsComponent = <EntityList list={list}/>
|
songsComponent = <EntityList list={list}/>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let paginateComponent;
|
let paginateComponent;
|
||||||
if (paginate) {
|
if (paginate) {
|
||||||
const total = paginate.total;
|
const total = paginate.total;
|
||||||
const currentPage = paginate.current_page;
|
const currentPage = paginate.current_page;
|
||||||
const pageLimit = paginate.per_page;
|
const pageLimit = paginate.per_page;
|
||||||
|
|
||||||
paginateComponent = <Paginate totalRecords={total} pageLimit={pageLimit} currentPage={currentPage} pageNeighbours={2} onPageChanged={handlePageChange} makeLink={makeLink}/>
|
paginateComponent = <Paginate totalRecords={total} pageLimit={pageLimit} currentPage={currentPage} pageNeighbours={2} onPageChanged={handlePageChange} makeLink={makeLink}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ul className='entity_list'>
|
<ul className='entity_list'>
|
||||||
{songsComponent}
|
{songsComponent}
|
||||||
</ul>
|
</ul>
|
||||||
{paginateComponent}
|
{paginateComponent}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const SearchDiscs = (props) => {
|
const SearchDiscs = (props) => {
|
||||||
const [discs, setDiscs] = useState(null);
|
const [discs, setDiscs] = useState(null);
|
||||||
const [paginate, setPaginate] = useState(null);
|
const [paginate, setPaginate] = useState(null);
|
||||||
const [page, setPage] = useState(null);
|
const [page, setPage] = useState(null);
|
||||||
|
|
||||||
useEffect(_ => {
|
useEffect(_ => {
|
||||||
setDiscs(null)
|
setDiscs(null)
|
||||||
setPaginate(null)
|
setPaginate(null)
|
||||||
loadDiscs(props.query, page)
|
loadDiscs(props.query, page)
|
||||||
}, [props.query, page])
|
}, [props.query, page])
|
||||||
|
|
||||||
const makeLink = page => `/search/disc?query=${props.query}&page=${page}`;
|
const makeLink = page => `/search/disc?query=${props.query}&page=${page}`;
|
||||||
|
|
||||||
const handlePageChange = page => {
|
const handlePageChange = page => {
|
||||||
setDiscs(null);
|
setDiscs(null);
|
||||||
setPage(page);
|
setPage(page);
|
||||||
props.onPageChange(page);
|
props.onPageChange(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadDiscs = (query, page) => {
|
const loadDiscs = (query, page) => {
|
||||||
page = page ? page : 1;
|
page = page ? page : 1;
|
||||||
|
|
||||||
searchDisc(query, page, 16).then((response) => {
|
searchDisc(query, page, 16).then((response) => {
|
||||||
setDiscs(response.discs);
|
setDiscs(response.discs);
|
||||||
setPaginate(response.paginate);
|
setPaginate(response.paginate);
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
let discsComponent = <EntityList placeholder={true} grid={true} size={16} cover={true}/>;
|
let discsComponent = <EntityList placeholder={true} grid={true} size={16} cover={true}/>;
|
||||||
if (discs) {
|
if (discs) {
|
||||||
const items = discs.map((disc) => ({
|
const items = discs.map((disc) => ({
|
||||||
'cover': <CoverArt disc={disc} size={3}/>,
|
'cover': <CoverArt disc={disc} size={3}/>,
|
||||||
'link': `/disc/${disc.id}`,
|
'link': `/disc/${disc.id}`,
|
||||||
'title': disc.title,
|
'title': disc.title,
|
||||||
'subtitle': disc.artist.name
|
'subtitle': disc.artist.name
|
||||||
}));
|
}));
|
||||||
const list = [{
|
const list = [{
|
||||||
'items': items
|
'items': items
|
||||||
}]
|
}]
|
||||||
discsComponent = <EntityList list={list} grid={true}/>
|
discsComponent = <EntityList list={list} grid={true}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
let paginateComponent;
|
let paginateComponent;
|
||||||
if (paginate) {
|
if (paginate) {
|
||||||
const total = paginate.total;
|
const total = paginate.total;
|
||||||
const currentPage = paginate.current_page;
|
const currentPage = paginate.current_page;
|
||||||
const pageLimit = paginate.per_page;
|
const pageLimit = paginate.per_page;
|
||||||
|
|
||||||
paginateComponent = <Paginate totalRecords={total} pageLimit={pageLimit} currentPage={currentPage} pageNeighbours={2} onPageChanged={handlePageChange} makeLink={makeLink}/>
|
paginateComponent = <Paginate totalRecords={total} pageLimit={pageLimit} currentPage={currentPage} pageNeighbours={2} onPageChanged={handlePageChange} makeLink={makeLink}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ul className='entity_list'>
|
<ul className='entity_list'>
|
||||||
{discsComponent}
|
{discsComponent}
|
||||||
</ul>
|
</ul>
|
||||||
{paginateComponent}
|
{paginateComponent}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const SearchArtists = (props) => {
|
const SearchArtists = (props) => {
|
||||||
const [artists, setArtist] = useState(null);
|
const [artists, setArtist] = useState(null);
|
||||||
const [paginate, setPaginate] = useState(null);
|
const [paginate, setPaginate] = useState(null);
|
||||||
const [page, setPage] = useState(props.page ? props.page : 1);
|
const [page, setPage] = useState(props.page ? props.page : 1);
|
||||||
|
|
||||||
useEffect(_ => {
|
useEffect(_ => {
|
||||||
setArtist(null)
|
setArtist(null)
|
||||||
setPaginate(null)
|
setPaginate(null)
|
||||||
loadArtists(props.query, page)
|
loadArtists(props.query, page)
|
||||||
}, [props.query, page])
|
}, [props.query, page])
|
||||||
|
|
||||||
const makeLink = page => `/search/artist?query=${props.query}&page=${page}`;
|
const makeLink = page => `/search/artist?query=${props.query}&page=${page}`;
|
||||||
|
|
||||||
const handlePageChange = page => {
|
const handlePageChange = page => {
|
||||||
setArtist(null);
|
setArtist(null);
|
||||||
setPage(page);
|
setPage(page);
|
||||||
props.onPageChange(page);
|
props.onPageChange(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadArtists = (query, page) => {
|
const loadArtists = (query, page) => {
|
||||||
page = page ? page : 1;
|
page = page ? page : 1;
|
||||||
|
|
||||||
searchArtist(query, page).then((response) => {
|
searchArtist(query, page).then((response) => {
|
||||||
setArtist(response.artists);
|
setArtist(response.artists);
|
||||||
setPaginate(response.paginate);
|
setPaginate(response.paginate);
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
let artistsContent = <EntityList placeholder={true} size={10}/>;
|
let artistsContent = <EntityList placeholder={true} size={10}/>;
|
||||||
if (artists) {
|
if (artists) {
|
||||||
const items = artists.map((artist) => ({
|
const items = artists.map((artist) => ({
|
||||||
'cover': null,
|
'cover': null,
|
||||||
'link': `/artist/${artist.id}`,
|
'link': `/artist/${artist.id}`,
|
||||||
'title': artist.name,
|
'title': artist.name,
|
||||||
'subtitle': [artist.type, artist.country].filter(Boolean).join(' - ')
|
'subtitle': [artist.type, artist.country].filter(Boolean).join(' - ')
|
||||||
}));
|
}));
|
||||||
const list = [{
|
const list = [{
|
||||||
'items': items
|
'items': items
|
||||||
}];
|
}];
|
||||||
artistsContent = <EntityList list={list}/>
|
artistsContent = <EntityList list={list}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
let paginateContent;
|
let paginateContent;
|
||||||
if (paginate) {
|
if (paginate) {
|
||||||
const total = paginate.total;
|
const total = paginate.total;
|
||||||
const currentPage = paginate.current_page;
|
const currentPage = paginate.current_page;
|
||||||
const pageLimit = paginate.per_page;
|
const pageLimit = paginate.per_page;
|
||||||
|
|
||||||
paginateContent = <Paginate totalRecords={total} pageLimit={pageLimit} currentPage={currentPage} pageNeighbours={2} onPageChanged={handlePageChange} makeLink={makeLink}/>
|
paginateContent = <Paginate totalRecords={total} pageLimit={pageLimit} currentPage={currentPage} pageNeighbours={2} onPageChanged={handlePageChange} makeLink={makeLink}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Grid>
|
||||||
<ul className='entity_list'>
|
<Row>
|
||||||
{artistsContent}
|
<Col>
|
||||||
</ul>
|
<ul className='entity_list'>
|
||||||
{paginateContent}
|
{artistsContent}
|
||||||
</Fragment>
|
</ul>
|
||||||
)
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
{paginateContent}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const SearchTabs = (props) => {
|
const SearchTabs = (props) => {
|
||||||
const nameToIndex = (name) => {
|
const nameToIndex = (name) => {
|
||||||
if (name === 'artist') return 0
|
if (name === 'artist') return 0
|
||||||
if (name === 'disc') return 1
|
if (name === 'disc') return 1
|
||||||
if (name === 'song') return 2
|
if (name === 'song') return 2
|
||||||
else return 0;
|
else return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const indexToName = (index) => {
|
const indexToName = (index) => {
|
||||||
if (index === 0) return 'artist'
|
if (index === 0) return 'artist'
|
||||||
if (index === 1) return 'disc'
|
if (index === 1) return 'disc'
|
||||||
if (index === 2) return 'song'
|
if (index === 2) return 'song'
|
||||||
else return 'artist';
|
else return 'artist';
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSelect = (index) => {
|
const handleSelect = (index) => {
|
||||||
props.onTabChanged(indexToName(index))
|
props.onTabChanged(indexToName(index))
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePageChange = (who) => (index) => props.onPageChange(who, index);
|
const handlePageChange = (who) => (index) => props.onPageChange(who, index);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs selectedIndex={nameToIndex(props.selected)} onSelect={handleSelect}>
|
<Tabs selectedIndex={nameToIndex(props.selected)} onSelect={handleSelect}>
|
||||||
<TabList className='tabs'>
|
<TabList className='tabs'>
|
||||||
<Tab className='tab' selectedClassName='selected'>Artistas</Tab>
|
<Tab className='tab' selectedClassName='selected'>Artistas</Tab>
|
||||||
<Tab className='tab' selectedClassName='selected'>Discos</Tab>
|
<Tab className='tab' selectedClassName='selected'>Discos</Tab>
|
||||||
<Tab className='tab' selectedClassName='selected'>Canciones</Tab>
|
<Tab className='tab' selectedClassName='selected'>Canciones</Tab>
|
||||||
</TabList>
|
</TabList>
|
||||||
<TabPanel><SearchArtists query={props.query} page={props.page} onPageChange={handlePageChange('artist')}/></TabPanel>
|
<TabPanel><SearchArtists query={props.query} page={props.page} onPageChange={handlePageChange('artist')}/></TabPanel>
|
||||||
<TabPanel><SearchDiscs query={props.query} page={props.page} onPageChange={handlePageChange('disc')}/></TabPanel>
|
<TabPanel><SearchDiscs query={props.query} page={props.page} onPageChange={handlePageChange('disc')}/></TabPanel>
|
||||||
<TabPanel><SearchSongs query={props.query} page={props.page} onPageChange={handlePageChange('song')}/></TabPanel>
|
<TabPanel><SearchSongs query={props.query} page={props.page} onPageChange={handlePageChange('song')}/></TabPanel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Search = (props) => {
|
export const Search = (props) => {
|
||||||
const parsedParams = queryString.parse(props.location.search);
|
const parsedParams = queryString.parse(props.location.search);
|
||||||
const [who, setWho] = useState(props.match.params['who'] ? props.match.params['who'] : 'artist')
|
const [who, setWho] = useState(props.match.params['who'] ? props.match.params['who'] : 'artist')
|
||||||
const [query, setQuery] = useState(parsedParams.query)
|
const [query, setQuery] = useState(parsedParams.query)
|
||||||
const [page, setPage] = useState(!isNaN(+parsedParams.page) ? +parsedParams.page : 1)
|
const [page, setPage] = useState(!isNaN(+parsedParams.page) ? +parsedParams.page : 1)
|
||||||
|
|
||||||
const navigateTo = (who, query, page) => props.history.push(`/search/${who}?query=${query}&page=${page}`);
|
const navigateTo = (who, query, page) => props.history.push(`/search/${who}?query=${query}&page=${page}`);
|
||||||
|
|
||||||
const handleQueryChange = query => {
|
const handleQueryChange = query => {
|
||||||
setQuery(query);
|
setQuery(query);
|
||||||
setPage(1);
|
setPage(1);
|
||||||
navigateTo(who, query, 1)
|
navigateTo(who, query, 1)
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTabChange = who => {
|
const handleTabChange = who => {
|
||||||
setWho(who);
|
setWho(who);
|
||||||
setPage(1)
|
setPage(1)
|
||||||
navigateTo(who, query, 1)
|
navigateTo(who, query, 1)
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePageChange = (who, page) => {
|
const handlePageChange = (who, page) => {
|
||||||
setWho(who);
|
setWho(who);
|
||||||
setPage(page);
|
setPage(page);
|
||||||
navigateTo(who, query, page);
|
navigateTo(who, query, page);
|
||||||
};
|
};
|
||||||
|
|
||||||
const content = _ => {
|
const content = (() => {
|
||||||
if (query) {
|
if (query) {
|
||||||
return <SearchTabs query={query} onTabChanged={handleTabChange} onPageChange={handlePageChange} selected={who} page={page}/>
|
return <SearchTabs query={query} onTabChanged={handleTabChange} onPageChange={handlePageChange} selected={who} page={page}/>
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Grid>
|
||||||
<h1>Búsqueda</h1>
|
<Row><Col><h1>Búsqueda</h1></Col></Row>
|
||||||
<SearchBar query={query} onQueryChanged={handleQueryChange} history={props.history}/>
|
<Row>
|
||||||
{content()}
|
<Col>
|
||||||
</Fragment>
|
<SearchBar query={query} onQueryChanged={handleQueryChange} history={props.history}/>
|
||||||
);
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
{content}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user