Fixed language options on all supported entrypoints
This commit is contained in:
@@ -8,6 +8,13 @@ import { BaseEndpoint } from './base';
|
||||
|
||||
const BASE_COLLECTION = '/collection';
|
||||
|
||||
export interface CollectionImageSearchOptions extends LanguageOption {
|
||||
/**
|
||||
* a list of ISO-639-1 values to query
|
||||
*/
|
||||
include_image_language?: string[],
|
||||
}
|
||||
|
||||
export class CollectionsEndpoint extends BaseEndpoint {
|
||||
constructor(protected readonly accessToken: string) {
|
||||
super(accessToken);
|
||||
@@ -23,10 +30,14 @@ export class CollectionsEndpoint extends BaseEndpoint {
|
||||
);
|
||||
}
|
||||
|
||||
async images(id: number, options?: LanguageOption): Promise<ImageCollection> {
|
||||
async images(id: number, options?: CollectionImageSearchOptions): Promise<ImageCollection> {
|
||||
const computedOptions = {
|
||||
include_image_language: options?.include_image_language?.join(','),
|
||||
language: options?.language,
|
||||
};
|
||||
return await this.api.get<ImageCollection>(
|
||||
`${BASE_COLLECTION}/${id}/images`,
|
||||
options
|
||||
computedOptions
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,13 @@ import {
|
||||
|
||||
const BASE_MOVIE = '/movie';
|
||||
|
||||
export interface MoviesImageSearchOptions extends LanguageOption {
|
||||
/**
|
||||
* a list of ISO-639-1 values to query
|
||||
*/
|
||||
include_image_language?: string[],
|
||||
}
|
||||
|
||||
export class MoviesEndpoint extends BaseEndpoint {
|
||||
constructor(protected readonly accessToken: string) {
|
||||
super(accessToken);
|
||||
@@ -38,12 +45,14 @@ export class MoviesEndpoint extends BaseEndpoint {
|
||||
|
||||
async details<T extends AppendToResponseMovieKey[] | undefined>(
|
||||
id: number,
|
||||
appendToResponse?: T
|
||||
appendToResponse?: T,
|
||||
language?: string
|
||||
) {
|
||||
const options = {
|
||||
append_to_response: appendToResponse
|
||||
? appendToResponse.join(',')
|
||||
: undefined,
|
||||
language: language,
|
||||
};
|
||||
|
||||
return await this.api.get<AppendToResponse<MovieDetails, T, 'movie'>>(
|
||||
@@ -68,16 +77,20 @@ export class MoviesEndpoint extends BaseEndpoint {
|
||||
);
|
||||
}
|
||||
|
||||
async credits(id: number): Promise<Credits> {
|
||||
return await this.api.get<Credits>(`${BASE_MOVIE}/${id}/credits`);
|
||||
async credits(id: number, options?: LanguageOption): Promise<Credits> {
|
||||
return await this.api.get<Credits>(`${BASE_MOVIE}/${id}/credits`, options);
|
||||
}
|
||||
|
||||
async externalIds(id: number): Promise<ExternalIds> {
|
||||
return await this.api.get<ExternalIds>(`${BASE_MOVIE}/${id}/external_ids`);
|
||||
}
|
||||
|
||||
async images(id: number): Promise<Images> {
|
||||
return await this.api.get<Images>(`${BASE_MOVIE}/${id}/images`);
|
||||
async images(id: number, options?: MoviesImageSearchOptions): Promise<Images> {
|
||||
const computedOptions = {
|
||||
include_image_language: options?.include_image_language?.join(','),
|
||||
language: options?.language,
|
||||
};
|
||||
return await this.api.get<Images>(`${BASE_MOVIE}/${id}/images`, computedOptions);
|
||||
}
|
||||
|
||||
async keywords(id: number): Promise<Keywords> {
|
||||
@@ -86,14 +99,14 @@ export class MoviesEndpoint extends BaseEndpoint {
|
||||
|
||||
async lists(
|
||||
id: number,
|
||||
options?: LanguageOption | PageOption
|
||||
options?: LanguageOption & PageOption
|
||||
): Promise<MovieLists> {
|
||||
return await this.api.get<MovieLists>(`${BASE_MOVIE}/${id}/lists`, options);
|
||||
}
|
||||
|
||||
async recommendations(
|
||||
id: number,
|
||||
options?: PageOption
|
||||
options?: LanguageOption & PageOption
|
||||
): Promise<Recommendations> {
|
||||
return await this.api.get<Recommendations>(
|
||||
`${BASE_MOVIE}/${id}/recommendations`,
|
||||
@@ -107,11 +120,11 @@ export class MoviesEndpoint extends BaseEndpoint {
|
||||
);
|
||||
}
|
||||
|
||||
async reviews(id: number, options?: PageOption): Promise<Reviews> {
|
||||
async reviews(id: number, options?: LanguageOption & PageOption): Promise<Reviews> {
|
||||
return await this.api.get<Reviews>(`${BASE_MOVIE}/${id}/reviews`, options);
|
||||
}
|
||||
|
||||
async similar(id: number, options?: PageOption): Promise<SimilarMovies> {
|
||||
async similar(id: number, options?: LanguageOption & PageOption): Promise<SimilarMovies> {
|
||||
return await this.api.get<SimilarMovies>(
|
||||
`${BASE_MOVIE}/${id}/similar`,
|
||||
options
|
||||
@@ -122,8 +135,8 @@ export class MoviesEndpoint extends BaseEndpoint {
|
||||
return await this.api.get<Translations>(`${BASE_MOVIE}/${id}/translations`);
|
||||
}
|
||||
|
||||
async videos(id: number): Promise<Videos> {
|
||||
return await this.api.get<Videos>(`${BASE_MOVIE}/${id}/videos`);
|
||||
async videos(id: number, options?: LanguageOption): Promise<Videos> {
|
||||
return await this.api.get<Videos>(`${BASE_MOVIE}/${id}/videos`, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +162,7 @@ export class MoviesEndpoint extends BaseEndpoint {
|
||||
);
|
||||
}
|
||||
|
||||
async popular(options?: PageOption): Promise<PopularMovies> {
|
||||
async popular(options?: LanguageOption & PageOption): Promise<PopularMovies> {
|
||||
return await this.api.get<PopularMovies>(`${BASE_MOVIE}/popular`, options);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
TaggedImages,
|
||||
Changes,
|
||||
PersonChangeValue,
|
||||
LanguageOption,
|
||||
} from '../types';
|
||||
import { BaseEndpoint } from './base';
|
||||
|
||||
@@ -26,12 +27,14 @@ export class PeopleEndpoint extends BaseEndpoint {
|
||||
|
||||
async details<T extends AppendToResponsePersonKey[] | undefined>(
|
||||
id: number,
|
||||
appendToResponse?: T
|
||||
appendToResponse?: T,
|
||||
language?: string
|
||||
) {
|
||||
const options = {
|
||||
append_to_response: appendToResponse
|
||||
? appendToResponse.join(',')
|
||||
: undefined,
|
||||
language: language
|
||||
};
|
||||
return await this.api.get<AppendToResponse<PersonDetails, T, 'person'>>(
|
||||
`${BASE_PERSON}/${id}`,
|
||||
@@ -49,21 +52,24 @@ export class PeopleEndpoint extends BaseEndpoint {
|
||||
);
|
||||
}
|
||||
|
||||
async movieCredits(id: number): Promise<PersonMovieCredit> {
|
||||
async movieCredits(id: number, options?: LanguageOption): Promise<PersonMovieCredit> {
|
||||
return await this.api.get<PersonMovieCredit>(
|
||||
`${BASE_PERSON}/${id}/movie_credits`
|
||||
`${BASE_PERSON}/${id}/movie_credits`,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
async tvShowCredits(id: number): Promise<PersonTvShowCredit> {
|
||||
async tvShowCredits(id: number, options?: LanguageOption): Promise<PersonTvShowCredit> {
|
||||
return await this.api.get<PersonTvShowCredit>(
|
||||
`${BASE_PERSON}/${id}/tv_credits`
|
||||
`${BASE_PERSON}/${id}/tv_credits`,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
async combinedCredits(id: number): Promise<PersonCombinedCredits> {
|
||||
async combinedCredits(id: number, options?: LanguageOption): Promise<PersonCombinedCredits> {
|
||||
return await this.api.get<PersonCombinedCredits>(
|
||||
`${BASE_PERSON}/${id}/combined_credits`
|
||||
`${BASE_PERSON}/${id}/combined_credits`,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
@@ -92,7 +98,7 @@ export class PeopleEndpoint extends BaseEndpoint {
|
||||
return await this.api.get<PersonDetails>(`${BASE_PERSON}/latest`);
|
||||
}
|
||||
|
||||
async popular(options?: PageOption): Promise<PopularPersons> {
|
||||
async popular(options?: LanguageOption & PageOption): Promise<PopularPersons> {
|
||||
return await this.api.get<PopularPersons>(
|
||||
`${BASE_PERSON}/popular`,
|
||||
options
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BaseEndpoint } from './base';
|
||||
import { MultiSearchResult, Search } from '../types/search';
|
||||
import { Collection, Company, Movie, Person, TV } from '../types';
|
||||
import { Collection, Company, LanguageOption, Movie, PageOption, Person, RegionOption, TV } from '../types';
|
||||
|
||||
const BASE_SEARCH = '/search';
|
||||
|
||||
@@ -9,22 +9,27 @@ export interface SearchOptions {
|
||||
page?: number;
|
||||
}
|
||||
|
||||
export interface MovieSearchOptions extends SearchOptions {
|
||||
export interface MovieSearchOptions extends SearchOptions, LanguageOption, PageOption, RegionOption {
|
||||
include_adult?: boolean;
|
||||
year?: number;
|
||||
primary_release_year?: number;
|
||||
}
|
||||
|
||||
export interface TvSearchOptions extends SearchOptions {
|
||||
export interface CollectionSearchOptions extends SearchOptions, LanguageOption, PageOption, RegionOption {
|
||||
include_adult?: boolean;
|
||||
}
|
||||
|
||||
export interface TvSearchOptions extends SearchOptions, LanguageOption, PageOption {
|
||||
include_adult?: boolean;
|
||||
year?: number;
|
||||
first_air_date_year?: number;
|
||||
}
|
||||
|
||||
export interface PeopleSearchOptions extends SearchOptions {
|
||||
export interface PeopleSearchOptions extends SearchOptions, LanguageOption, PageOption {
|
||||
include_adult?: boolean;
|
||||
}
|
||||
|
||||
export interface MultiSearchOptions extends SearchOptions {
|
||||
export interface MultiSearchOptions extends SearchOptions, LanguageOption, PageOption {
|
||||
include_adult?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TrendingMediaType, TimeWindow, TrendingResults } from '../types';
|
||||
import { TrendingMediaType, TimeWindow, TrendingResults, LanguageOption } from '../types';
|
||||
import { BaseEndpoint } from './base';
|
||||
|
||||
export class TrendingEndpoint extends BaseEndpoint {
|
||||
@@ -8,10 +8,12 @@ export class TrendingEndpoint extends BaseEndpoint {
|
||||
|
||||
async trending<T extends TrendingMediaType>(
|
||||
mediaType: T,
|
||||
timeWindow: TimeWindow
|
||||
timeWindow: TimeWindow,
|
||||
options?: LanguageOption
|
||||
): Promise<TrendingResults<T>> {
|
||||
return await this.api.get<TrendingResults<T>>(
|
||||
`/trending/${mediaType}/${timeWindow}`
|
||||
`/trending/${mediaType}/${timeWindow}`,
|
||||
options
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,20 @@ const BASE_EPISODE = (episodeSelection: EpisodeSelection): string => {
|
||||
return `/tv/${episodeSelection.tvShowID}/season/${episodeSelection.seasonNumber}/episode/${episodeSelection.episodeNumber}`;
|
||||
};
|
||||
|
||||
export interface TvEpisodeImageSearchOptions extends LanguageOption {
|
||||
/**
|
||||
* a list of ISO-639-1 values to query
|
||||
*/
|
||||
include_image_language?: string[],
|
||||
}
|
||||
|
||||
export interface TvEpisodeVideoSearchOptions extends LanguageOption {
|
||||
/**
|
||||
* a list of ISO-639-1 values to query
|
||||
*/
|
||||
include_video_language?: string[],
|
||||
}
|
||||
|
||||
export class TvEpisodesEndpoint extends BaseEndpoint {
|
||||
constructor(accessToken: string) {
|
||||
super(accessToken);
|
||||
@@ -62,9 +76,14 @@ export class TvEpisodesEndpoint extends BaseEndpoint {
|
||||
);
|
||||
}
|
||||
|
||||
async images(episodeSelection: EpisodeSelection) {
|
||||
async images(episodeSelection: EpisodeSelection, options?: TvEpisodeImageSearchOptions) {
|
||||
const computedOptions = {
|
||||
include_image_language: options?.include_image_language?.join(','),
|
||||
language: options?.language,
|
||||
};
|
||||
return await this.api.get<Images>(
|
||||
`${BASE_EPISODE(episodeSelection)}/images`
|
||||
`${BASE_EPISODE(episodeSelection)}/images`,
|
||||
computedOptions
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,10 +93,14 @@ export class TvEpisodesEndpoint extends BaseEndpoint {
|
||||
);
|
||||
}
|
||||
|
||||
async videos(episodeSelection: EpisodeSelection, options?: LanguageOption) {
|
||||
async videos(episodeSelection: EpisodeSelection, options?: TvEpisodeVideoSearchOptions) {
|
||||
const computedOptions = {
|
||||
include_video_language: options?.include_video_language?.join(','),
|
||||
language: options?.language,
|
||||
};
|
||||
return await this.api.get<Videos>(
|
||||
`${BASE_EPISODE(episodeSelection)}/videos`,
|
||||
options
|
||||
computedOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,20 @@ const BASE_SEASON = (seasonSelection: SeasonSelection): string => {
|
||||
return `/tv/${seasonSelection.tvShowID}/season/${seasonSelection.seasonNumber}`;
|
||||
};
|
||||
|
||||
export interface TvSeasonImageSearchOptions extends LanguageOption {
|
||||
/**
|
||||
* a list of ISO-639-1 values to query
|
||||
*/
|
||||
include_image_language?: string[],
|
||||
}
|
||||
|
||||
export interface TvSeasonVideoSearchOptions extends LanguageOption {
|
||||
/**
|
||||
* a list of ISO-639-1 values to query
|
||||
*/
|
||||
include_video_language?: string[],
|
||||
}
|
||||
|
||||
export class TvSeasonsEndpoint extends BaseEndpoint {
|
||||
constructor(accessToken: string) {
|
||||
super(accessToken);
|
||||
@@ -77,17 +91,25 @@ export class TvSeasonsEndpoint extends BaseEndpoint {
|
||||
);
|
||||
}
|
||||
|
||||
async images(seasonSelection: SeasonSelection, options?: LanguageOption) {
|
||||
async images(seasonSelection: SeasonSelection, options?: TvSeasonImageSearchOptions) {
|
||||
const computedOptions = {
|
||||
include_image_language: options?.include_image_language?.join(','),
|
||||
language: options?.language,
|
||||
};
|
||||
return await this.api.get<Images>(
|
||||
`${BASE_SEASON(seasonSelection)}/images`,
|
||||
options
|
||||
computedOptions
|
||||
);
|
||||
}
|
||||
|
||||
async videos(seasonSelection: SeasonSelection, options?: LanguageOption) {
|
||||
async videos(seasonSelection: SeasonSelection, options?: TvSeasonVideoSearchOptions) {
|
||||
const computedOptions = {
|
||||
include_video_language: options?.include_video_language?.join(','),
|
||||
language: options?.language,
|
||||
};
|
||||
return await this.api.get<Videos>(
|
||||
`${BASE_SEASON(seasonSelection)}/videos`,
|
||||
options
|
||||
computedOptions
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
ScreenedTheatrically,
|
||||
SeasonDetails,
|
||||
SimilarTvShows,
|
||||
TimezoneOption,
|
||||
TopRatedTvShows,
|
||||
Translations,
|
||||
TvShowChangeValue,
|
||||
@@ -34,6 +35,20 @@ import {
|
||||
|
||||
const BASE_TV = '/tv';
|
||||
|
||||
export interface TvImageSearchOptions extends LanguageOption {
|
||||
/**
|
||||
* a list of ISO-639-1 values to query
|
||||
*/
|
||||
include_image_language?: string[],
|
||||
}
|
||||
|
||||
export interface TvVideoSearchOptions extends LanguageOption {
|
||||
/**
|
||||
* a list of ISO-639-1 values to query
|
||||
*/
|
||||
include_video_language?: string[],
|
||||
}
|
||||
|
||||
export class TvShowsEndpoint extends BaseEndpoint {
|
||||
constructor(protected readonly accessToken: string) {
|
||||
super(accessToken);
|
||||
@@ -41,12 +56,14 @@ export class TvShowsEndpoint extends BaseEndpoint {
|
||||
|
||||
async details<T extends AppendToResponseTvKey[] | undefined>(
|
||||
id: number,
|
||||
appendToResponse?: T
|
||||
appendToResponse?: T,
|
||||
language?: string
|
||||
) {
|
||||
const options = {
|
||||
append_to_response: appendToResponse
|
||||
? appendToResponse.join(',')
|
||||
: undefined,
|
||||
language: language,
|
||||
};
|
||||
return await this.api.get<AppendToResponse<TvShowDetails, T, 'tvShow'>>(
|
||||
`${BASE_TV}/${id}`,
|
||||
@@ -76,14 +93,15 @@ export class TvShowsEndpoint extends BaseEndpoint {
|
||||
);
|
||||
}
|
||||
|
||||
async aggregateCredits(id: number): Promise<AggregateCredits> {
|
||||
async aggregateCredits(id: number, options?: LanguageOption): Promise<AggregateCredits> {
|
||||
return await this.api.get<AggregateCredits>(
|
||||
`${BASE_TV}/${id}/aggregate_credits`
|
||||
`${BASE_TV}/${id}/aggregate_credits`,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
async credits(id: number): Promise<Credits> {
|
||||
return await this.api.get<Credits>(`${BASE_TV}/${id}/credits`);
|
||||
async credits(id: number, options?: LanguageOption): Promise<Credits> {
|
||||
return await this.api.get<Credits>(`${BASE_TV}/${id}/credits`, options);
|
||||
}
|
||||
|
||||
async season(tvId: number, seasonNumber: number): Promise<SeasonDetails> {
|
||||
@@ -100,8 +118,12 @@ export class TvShowsEndpoint extends BaseEndpoint {
|
||||
return await this.api.get<ExternalIds>(`${BASE_TV}/${id}/external_ids`);
|
||||
}
|
||||
|
||||
async images(id: number): Promise<Images> {
|
||||
return await this.api.get<Images>(`${BASE_TV}/${id}/images`);
|
||||
async images(id: number, options?: TvImageSearchOptions): Promise<Images> {
|
||||
const computedOptions = {
|
||||
include_image_language: options?.include_image_language?.join(','),
|
||||
language: options?.language,
|
||||
};
|
||||
return await this.api.get<Images>(`${BASE_TV}/${id}/images`, computedOptions);
|
||||
}
|
||||
|
||||
async keywords(id: number): Promise<Keywords> {
|
||||
@@ -110,7 +132,7 @@ export class TvShowsEndpoint extends BaseEndpoint {
|
||||
|
||||
async recommendations(
|
||||
id: number,
|
||||
options?: PageOption
|
||||
options?: LanguageOption & PageOption
|
||||
): Promise<Recommendations> {
|
||||
return await this.api.get<Recommendations>(
|
||||
`${BASE_TV}/${id}/recommendations`,
|
||||
@@ -118,7 +140,7 @@ export class TvShowsEndpoint extends BaseEndpoint {
|
||||
);
|
||||
}
|
||||
|
||||
async reviews(id: number, options?: PageOption): Promise<Reviews> {
|
||||
async reviews(id: number, options?: LanguageOption | PageOption): Promise<Reviews> {
|
||||
return await this.api.get<Reviews>(`${BASE_TV}/${id}/reviews`, options);
|
||||
}
|
||||
|
||||
@@ -128,7 +150,7 @@ export class TvShowsEndpoint extends BaseEndpoint {
|
||||
);
|
||||
}
|
||||
|
||||
async similar(id: number, options?: PageOption): Promise<SimilarTvShows> {
|
||||
async similar(id: number, options?: LanguageOption | PageOption): Promise<SimilarTvShows> {
|
||||
return await this.api.get<SimilarTvShows>(
|
||||
`${BASE_TV}/${id}/similar`,
|
||||
options
|
||||
@@ -139,8 +161,12 @@ export class TvShowsEndpoint extends BaseEndpoint {
|
||||
return await this.api.get<Translations>(`${BASE_TV}/${id}/translations`);
|
||||
}
|
||||
|
||||
async videos(id: number): Promise<Videos> {
|
||||
return await this.api.get<Videos>(`${BASE_TV}/${id}/videos`);
|
||||
async videos(id: number, options?: TvVideoSearchOptions): Promise<Videos> {
|
||||
const computedOptions = {
|
||||
include_video_language: options?.include_video_language?.join(','),
|
||||
language: options?.language,
|
||||
};
|
||||
return await this.api.get<Videos>(`${BASE_TV}/${id}/videos`, computedOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,12 +183,14 @@ export class TvShowsEndpoint extends BaseEndpoint {
|
||||
return await this.api.get<LatestTvShows>(`${BASE_TV}/latest`);
|
||||
}
|
||||
|
||||
async onTheAir(): Promise<OnTheAir> {
|
||||
return await this.api.get<OnTheAir>(`${BASE_TV}/on_the_air`);
|
||||
async onTheAir(
|
||||
options?: PageOption & LanguageOption & TimezoneOption
|
||||
): Promise<OnTheAir> {
|
||||
return await this.api.get<OnTheAir>(`${BASE_TV}/on_the_air`, options);
|
||||
}
|
||||
|
||||
async airingToday(
|
||||
options?: PageOption & LanguageOption & RegionOption
|
||||
options?: PageOption & LanguageOption & TimezoneOption
|
||||
): Promise<TvShowsAiringToday> {
|
||||
return await this.api.get<TvShowsAiringToday>(
|
||||
`${BASE_TV}/airing_today`,
|
||||
@@ -171,13 +199,13 @@ export class TvShowsEndpoint extends BaseEndpoint {
|
||||
}
|
||||
|
||||
async popular(
|
||||
options?: PageOption & LanguageOption & RegionOption
|
||||
options?: PageOption & LanguageOption
|
||||
): Promise<PopularTvShows> {
|
||||
return await this.api.get<PopularTvShows>(`${BASE_TV}/popular`, options);
|
||||
}
|
||||
|
||||
async topRated(
|
||||
options?: PageOption & LanguageOption & RegionOption
|
||||
options?: PageOption & LanguageOption
|
||||
): Promise<TopRatedTvShows> {
|
||||
return await this.api.get<TopRatedTvShows>(`${BASE_TV}/top_rated`, options);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Movie } from '.';
|
||||
import { PageOption } from './options';
|
||||
|
||||
export interface KeywordsOptions {
|
||||
export interface KeywordsOptions extends PageOption {
|
||||
include_adult?: boolean;
|
||||
language?: string;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ export interface RegionOption {
|
||||
region?: string;
|
||||
}
|
||||
|
||||
export interface TimezoneOption {
|
||||
timezone?: string;
|
||||
}
|
||||
|
||||
export interface PageOption {
|
||||
page?: number;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user