Merge branch 'fix_frontend_duplicate_requests' of https://github.com/masterstur/lemmy into masterstur-fix_frontend_duplicate_requests
This commit is contained in:
commit
d6fdfe0b6d
10 changed files with 630 additions and 453 deletions
41
ui/src/components/communities.tsx
vendored
41
ui/src/components/communities.tsx
vendored
|
@ -13,7 +13,7 @@ import {
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
import { WebSocketService } from '../services';
|
import { WebSocketService } from '../services';
|
||||||
import { wsJsonToRes, toast } from '../utils';
|
import { wsJsonToRes, toast, getPageFromProps } from '../utils';
|
||||||
import { CommunityLink } from './community-link';
|
import { CommunityLink } from './community-link';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
|
|
||||||
|
@ -27,12 +27,16 @@ interface CommunitiesState {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CommunitiesProps {
|
||||||
|
page: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class Communities extends Component<any, CommunitiesState> {
|
export class Communities extends Component<any, CommunitiesState> {
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: CommunitiesState = {
|
private emptyState: CommunitiesState = {
|
||||||
communities: [],
|
communities: [],
|
||||||
loading: true,
|
loading: true,
|
||||||
page: this.getPageFromProps(this.props),
|
page: getPageFromProps(this.props),
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -50,19 +54,19 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
WebSocketService.Instance.getSite();
|
WebSocketService.Instance.getSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
getPageFromProps(props: any): number {
|
|
||||||
return props.match.params.page ? Number(props.match.params.page) : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.subscription.unsubscribe();
|
this.subscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Necessary for back button for some reason
|
static getDerivedStateFromProps(props: any): CommunitiesProps {
|
||||||
componentWillReceiveProps(nextProps: any) {
|
return {
|
||||||
if (nextProps.history.action == 'POP') {
|
page: getPageFromProps(props),
|
||||||
this.state = this.emptyState;
|
};
|
||||||
this.state.page = this.getPageFromProps(nextProps);
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(_: any, lastState: CommunitiesState) {
|
||||||
|
if (lastState.page !== this.state.page) {
|
||||||
|
this.setState({ loading: true });
|
||||||
this.refetch();
|
this.refetch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,22 +176,17 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUrl() {
|
updateUrl(paramUpdates: CommunitiesProps) {
|
||||||
this.props.history.push(`/communities/page/${this.state.page}`);
|
const page = paramUpdates.page || this.state.page;
|
||||||
|
this.props.history.push(`/communities/page/${page}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
nextPage(i: Communities) {
|
nextPage(i: Communities) {
|
||||||
i.state.page++;
|
i.updateUrl({ page: i.state.page + 1 });
|
||||||
i.setState(i.state);
|
|
||||||
i.updateUrl();
|
|
||||||
i.refetch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prevPage(i: Communities) {
|
prevPage(i: Communities) {
|
||||||
i.state.page--;
|
i.updateUrl({ page: i.state.page - 1 });
|
||||||
i.setState(i.state);
|
|
||||||
i.updateUrl();
|
|
||||||
i.refetch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnsubscribe(communityId: number) {
|
handleUnsubscribe(communityId: number) {
|
||||||
|
|
68
ui/src/components/community.tsx
vendored
68
ui/src/components/community.tsx
vendored
|
@ -65,6 +65,18 @@ interface State {
|
||||||
site: Site;
|
site: Site;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CommunityProps {
|
||||||
|
dataType: DataType;
|
||||||
|
sort: SortType;
|
||||||
|
page: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UrlParams {
|
||||||
|
dataType?: string;
|
||||||
|
sort?: string;
|
||||||
|
page?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class Community extends Component<any, State> {
|
export class Community extends Component<any, State> {
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: State = {
|
private emptyState: State = {
|
||||||
|
@ -143,16 +155,21 @@ export class Community extends Component<any, State> {
|
||||||
this.subscription.unsubscribe();
|
this.subscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Necessary for back button for some reason
|
static getDerivedStateFromProps(props: any): CommunityProps {
|
||||||
componentWillReceiveProps(nextProps: any) {
|
return {
|
||||||
|
dataType: getDataTypeFromProps(props),
|
||||||
|
sort: getSortTypeFromProps(props),
|
||||||
|
page: getPageFromProps(props),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(_: any, lastState: State) {
|
||||||
if (
|
if (
|
||||||
nextProps.history.action == 'POP' ||
|
lastState.dataType !== this.state.dataType ||
|
||||||
nextProps.history.action == 'PUSH'
|
lastState.sort !== this.state.sort ||
|
||||||
|
lastState.page !== this.state.page
|
||||||
) {
|
) {
|
||||||
this.state.dataType = getDataTypeFromProps(nextProps);
|
this.setState({ loading: true });
|
||||||
this.state.sort = getSortTypeFromProps(nextProps);
|
|
||||||
this.state.page = getPageFromProps(nextProps);
|
|
||||||
this.setState(this.state);
|
|
||||||
this.fetchData();
|
this.fetchData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,46 +290,33 @@ export class Community extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
nextPage(i: Community) {
|
nextPage(i: Community) {
|
||||||
i.state.page++;
|
i.updateUrl({ page: i.state.page + 1 });
|
||||||
i.setState(i.state);
|
|
||||||
i.updateUrl();
|
|
||||||
i.fetchData();
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
prevPage(i: Community) {
|
prevPage(i: Community) {
|
||||||
i.state.page--;
|
i.updateUrl({ page: i.state.page - 1 });
|
||||||
i.setState(i.state);
|
|
||||||
i.updateUrl();
|
|
||||||
i.fetchData();
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSortChange(val: SortType) {
|
handleSortChange(val: SortType) {
|
||||||
this.state.sort = val;
|
this.updateUrl({ sort: SortType[val].toLowerCase(), page: 1 });
|
||||||
this.state.page = 1;
|
|
||||||
this.state.loading = true;
|
|
||||||
this.setState(this.state);
|
|
||||||
this.updateUrl();
|
|
||||||
this.fetchData();
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDataTypeChange(val: DataType) {
|
handleDataTypeChange(val: DataType) {
|
||||||
this.state.dataType = val;
|
this.updateUrl({ dataType: DataType[val].toLowerCase(), page: 1 });
|
||||||
this.state.page = 1;
|
|
||||||
this.state.loading = true;
|
|
||||||
this.setState(this.state);
|
|
||||||
this.updateUrl();
|
|
||||||
this.fetchData();
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUrl() {
|
updateUrl(paramUpdates: UrlParams) {
|
||||||
let dataTypeStr = DataType[this.state.dataType].toLowerCase();
|
const dataTypeStr =
|
||||||
let sortStr = SortType[this.state.sort].toLowerCase();
|
paramUpdates.dataType || DataType[this.state.dataType].toLowerCase();
|
||||||
|
const sortStr =
|
||||||
|
paramUpdates.sort || SortType[this.state.sort].toLowerCase();
|
||||||
|
const page = paramUpdates.page || this.state.page;
|
||||||
this.props.history.push(
|
this.props.history.push(
|
||||||
`/c/${this.state.community.name}/data_type/${dataTypeStr}/sort/${sortStr}/page/${this.state.page}`
|
`/c/${this.state.community.name}/data_type/${dataTypeStr}/sort/${sortStr}/page/${page}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
ui/src/components/data-type-select.tsx
vendored
15
ui/src/components/data-type-select.tsx
vendored
|
@ -25,6 +25,12 @@ export class DataTypeSelect extends Component<
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getDerivedStateFromProps(props) {
|
||||||
|
return {
|
||||||
|
type_: props.type_,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="btn-group btn-group-toggle">
|
<div class="btn-group btn-group-toggle">
|
||||||
|
@ -42,8 +48,9 @@ export class DataTypeSelect extends Component<
|
||||||
{i18n.t('posts')}
|
{i18n.t('posts')}
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
className={`pointer btn btn-sm btn-secondary ${this.state.type_ ==
|
className={`pointer btn btn-sm btn-secondary ${
|
||||||
DataType.Comment && 'active'}`}
|
this.state.type_ == DataType.Comment && 'active'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
|
@ -58,8 +65,6 @@ export class DataTypeSelect extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTypeChange(i: DataTypeSelect, event: any) {
|
handleTypeChange(i: DataTypeSelect, event: any) {
|
||||||
i.state.type_ = Number(event.target.value);
|
i.props.onChange(Number(event.target.value));
|
||||||
i.setState(i.state);
|
|
||||||
i.props.onChange(i.state.type_);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
15
ui/src/components/listing-type-select.tsx
vendored
15
ui/src/components/listing-type-select.tsx
vendored
|
@ -26,6 +26,12 @@ export class ListingTypeSelect extends Component<
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getDerivedStateFromProps(props) {
|
||||||
|
return {
|
||||||
|
type_: props.type_,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="btn-group btn-group-toggle">
|
<div class="btn-group btn-group-toggle">
|
||||||
|
@ -45,8 +51,9 @@ export class ListingTypeSelect extends Component<
|
||||||
{i18n.t('subscribed')}
|
{i18n.t('subscribed')}
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
className={`pointer btn btn-sm btn-secondary ${this.state.type_ ==
|
className={`pointer btn btn-sm btn-secondary ${
|
||||||
ListingType.All && 'active'}`}
|
this.state.type_ == ListingType.All && 'active'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
|
@ -61,8 +68,6 @@ export class ListingTypeSelect extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTypeChange(i: ListingTypeSelect, event: any) {
|
handleTypeChange(i: ListingTypeSelect, event: any) {
|
||||||
i.state.type_ = Number(event.target.value);
|
i.props.onChange(Number(event.target.value));
|
||||||
i.setState(i.state);
|
|
||||||
i.props.onChange(i.state.type_);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
86
ui/src/components/main.tsx
vendored
86
ui/src/components/main.tsx
vendored
|
@ -70,6 +70,20 @@ interface MainState {
|
||||||
page: number;
|
page: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface MainProps {
|
||||||
|
listingType: ListingType;
|
||||||
|
dataType: DataType;
|
||||||
|
sort: SortType;
|
||||||
|
page: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UrlParams {
|
||||||
|
listingType?: string;
|
||||||
|
dataType?: string;
|
||||||
|
sort?: string;
|
||||||
|
page?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class Main extends Component<any, MainState> {
|
export class Main extends Component<any, MainState> {
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: MainState = {
|
private emptyState: MainState = {
|
||||||
|
@ -141,17 +155,23 @@ export class Main extends Component<any, MainState> {
|
||||||
this.subscription.unsubscribe();
|
this.subscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Necessary for back button for some reason
|
static getDerivedStateFromProps(props: any): MainProps {
|
||||||
componentWillReceiveProps(nextProps: any) {
|
return {
|
||||||
|
listingType: getListingTypeFromProps(props),
|
||||||
|
dataType: getDataTypeFromProps(props),
|
||||||
|
sort: getSortTypeFromProps(props),
|
||||||
|
page: getPageFromProps(props),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(_: any, lastState: MainState) {
|
||||||
if (
|
if (
|
||||||
nextProps.history.action == 'POP' ||
|
lastState.listingType !== this.state.listingType ||
|
||||||
nextProps.history.action == 'PUSH'
|
lastState.dataType !== this.state.dataType ||
|
||||||
|
lastState.sort !== this.state.sort ||
|
||||||
|
lastState.page !== this.state.page
|
||||||
) {
|
) {
|
||||||
this.state.listingType = getListingTypeFromProps(nextProps);
|
this.setState({ loading: true });
|
||||||
this.state.dataType = getDataTypeFromProps(nextProps);
|
|
||||||
this.state.sort = getSortTypeFromProps(nextProps);
|
|
||||||
this.state.page = getPageFromProps(nextProps);
|
|
||||||
this.setState(this.state);
|
|
||||||
this.fetchData();
|
this.fetchData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,12 +277,17 @@ export class Main extends Component<any, MainState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUrl() {
|
updateUrl(paramUpdates: UrlParams) {
|
||||||
let listingTypeStr = ListingType[this.state.listingType].toLowerCase();
|
const listingTypeStr =
|
||||||
let dataTypeStr = DataType[this.state.dataType].toLowerCase();
|
paramUpdates.listingType ||
|
||||||
let sortStr = SortType[this.state.sort].toLowerCase();
|
ListingType[this.state.listingType].toLowerCase();
|
||||||
|
const dataTypeStr =
|
||||||
|
paramUpdates.dataType || DataType[this.state.dataType].toLowerCase();
|
||||||
|
const sortStr =
|
||||||
|
paramUpdates.sort || SortType[this.state.sort].toLowerCase();
|
||||||
|
const page = paramUpdates.page || this.state.page;
|
||||||
this.props.history.push(
|
this.props.history.push(
|
||||||
`/home/data_type/${dataTypeStr}/listing_type/${listingTypeStr}/sort/${sortStr}/page/${this.state.page}`
|
`/home/data_type/${dataTypeStr}/listing_type/${listingTypeStr}/sort/${sortStr}/page/${page}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,50 +554,27 @@ export class Main extends Component<any, MainState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
nextPage(i: Main) {
|
nextPage(i: Main) {
|
||||||
i.state.page++;
|
i.updateUrl({ page: this.state.page + 1 });
|
||||||
i.state.loading = true;
|
|
||||||
i.setState(i.state);
|
|
||||||
i.updateUrl();
|
|
||||||
i.fetchData();
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
prevPage(i: Main) {
|
prevPage(i: Main) {
|
||||||
i.state.page--;
|
i.updateUrl({ page: this.state.page - 1 });
|
||||||
i.state.loading = true;
|
|
||||||
i.setState(i.state);
|
|
||||||
i.updateUrl();
|
|
||||||
i.fetchData();
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSortChange(val: SortType) {
|
handleSortChange(val: SortType) {
|
||||||
this.state.sort = val;
|
this.updateUrl({ sort: SortType[val].toLowerCase(), page: 1 });
|
||||||
this.state.page = 1;
|
|
||||||
this.state.loading = true;
|
|
||||||
this.setState(this.state);
|
|
||||||
this.updateUrl();
|
|
||||||
this.fetchData();
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleListingTypeChange(val: ListingType) {
|
handleListingTypeChange(val: ListingType) {
|
||||||
this.state.listingType = val;
|
this.updateUrl({ listingType: ListingType[val].toLowerCase(), page: 1 });
|
||||||
this.state.page = 1;
|
|
||||||
this.state.loading = true;
|
|
||||||
this.setState(this.state);
|
|
||||||
this.updateUrl();
|
|
||||||
this.fetchData();
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDataTypeChange(val: DataType) {
|
handleDataTypeChange(val: DataType) {
|
||||||
this.state.dataType = val;
|
this.updateUrl({ dataType: DataType[val].toLowerCase(), page: 1 });
|
||||||
this.state.page = 1;
|
|
||||||
this.state.loading = true;
|
|
||||||
this.setState(this.state);
|
|
||||||
this.updateUrl();
|
|
||||||
this.fetchData();
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
109
ui/src/components/search.tsx
vendored
109
ui/src/components/search.tsx
vendored
|
@ -28,6 +28,7 @@ import {
|
||||||
createCommentLikeRes,
|
createCommentLikeRes,
|
||||||
createPostLikeFindRes,
|
createPostLikeFindRes,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
|
getPageFromProps,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { PostListing } from './post-listing';
|
import { PostListing } from './post-listing';
|
||||||
import { UserListing } from './user-listing';
|
import { UserListing } from './user-listing';
|
||||||
|
@ -44,15 +45,31 @@ interface SearchState {
|
||||||
searchResponse: SearchResponse;
|
searchResponse: SearchResponse;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
site: Site;
|
site: Site;
|
||||||
|
searchText: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SearchProps {
|
||||||
|
q: string;
|
||||||
|
type_: SearchType;
|
||||||
|
sort: SortType;
|
||||||
|
page: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UrlParams {
|
||||||
|
q?: string;
|
||||||
|
type_?: string;
|
||||||
|
sort?: string;
|
||||||
|
page?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Search extends Component<any, SearchState> {
|
export class Search extends Component<any, SearchState> {
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: SearchState = {
|
private emptyState: SearchState = {
|
||||||
q: this.getSearchQueryFromProps(this.props),
|
q: Search.getSearchQueryFromProps(this.props),
|
||||||
type_: this.getSearchTypeFromProps(this.props),
|
type_: Search.getSearchTypeFromProps(this.props),
|
||||||
sort: this.getSortTypeFromProps(this.props),
|
sort: Search.getSortTypeFromProps(this.props),
|
||||||
page: this.getPageFromProps(this.props),
|
page: getPageFromProps(this.props),
|
||||||
|
searchText: Search.getSearchQueryFromProps(this.props),
|
||||||
searchResponse: {
|
searchResponse: {
|
||||||
type_: null,
|
type_: null,
|
||||||
posts: [],
|
posts: [],
|
||||||
|
@ -77,26 +94,22 @@ export class Search extends Component<any, SearchState> {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
getSearchQueryFromProps(props: any): string {
|
static getSearchQueryFromProps(props: any): string {
|
||||||
return props.match.params.q ? props.match.params.q : '';
|
return props.match.params.q ? props.match.params.q : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
getSearchTypeFromProps(props: any): SearchType {
|
static getSearchTypeFromProps(props: any): SearchType {
|
||||||
return props.match.params.type
|
return props.match.params.type
|
||||||
? routeSearchTypeToEnum(props.match.params.type)
|
? routeSearchTypeToEnum(props.match.params.type)
|
||||||
: SearchType.All;
|
: SearchType.All;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSortTypeFromProps(props: any): SortType {
|
static getSortTypeFromProps(props: any): SortType {
|
||||||
return props.match.params.sort
|
return props.match.params.sort
|
||||||
? routeSortTypeToEnum(props.match.params.sort)
|
? routeSortTypeToEnum(props.match.params.sort)
|
||||||
: SortType.TopAll;
|
: SortType.TopAll;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPageFromProps(props: any): number {
|
|
||||||
return props.match.params.page ? Number(props.match.params.page) : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
|
@ -122,17 +135,23 @@ export class Search extends Component<any, SearchState> {
|
||||||
this.subscription.unsubscribe();
|
this.subscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Necessary for back button for some reason
|
static getDerivedStateFromProps(props: any): SearchProps {
|
||||||
componentWillReceiveProps(nextProps: any) {
|
return {
|
||||||
|
q: Search.getSearchQueryFromProps(props),
|
||||||
|
type_: Search.getSearchTypeFromProps(props),
|
||||||
|
sort: Search.getSortTypeFromProps(props),
|
||||||
|
page: getPageFromProps(props),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(_: any, lastState: SearchState) {
|
||||||
if (
|
if (
|
||||||
nextProps.history.action == 'POP' ||
|
lastState.q !== this.state.q ||
|
||||||
nextProps.history.action == 'PUSH'
|
lastState.type_ !== this.state.type_ ||
|
||||||
|
lastState.sort !== this.state.sort ||
|
||||||
|
lastState.page !== this.state.page
|
||||||
) {
|
) {
|
||||||
this.state.q = this.getSearchQueryFromProps(nextProps);
|
this.setState({ loading: true, searchText: this.state.q });
|
||||||
this.state.type_ = this.getSearchTypeFromProps(nextProps);
|
|
||||||
this.state.sort = this.getSortTypeFromProps(nextProps);
|
|
||||||
this.state.page = this.getPageFromProps(nextProps);
|
|
||||||
this.setState(this.state);
|
|
||||||
this.search();
|
this.search();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,7 +182,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control mr-2"
|
class="form-control mr-2"
|
||||||
value={this.state.q}
|
value={this.state.searchText}
|
||||||
placeholder={`${i18n.t('search')}...`}
|
placeholder={`${i18n.t('search')}...`}
|
||||||
onInput={linkEvent(this, this.handleQChange)}
|
onInput={linkEvent(this, this.handleQChange)}
|
||||||
required
|
required
|
||||||
|
@ -413,17 +432,11 @@ export class Search extends Component<any, SearchState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
nextPage(i: Search) {
|
nextPage(i: Search) {
|
||||||
i.state.page++;
|
i.updateUrl({ page: i.state.page + 1 });
|
||||||
i.setState(i.state);
|
|
||||||
i.updateUrl();
|
|
||||||
i.search();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prevPage(i: Search) {
|
prevPage(i: Search) {
|
||||||
i.state.page--;
|
i.updateUrl({ page: i.state.page - 1 });
|
||||||
i.setState(i.state);
|
|
||||||
i.updateUrl();
|
|
||||||
i.search();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
search() {
|
search() {
|
||||||
|
@ -441,37 +454,39 @@ export class Search extends Component<any, SearchState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSortChange(val: SortType) {
|
handleSortChange(val: SortType) {
|
||||||
this.state.sort = val;
|
this.updateUrl({ sort: SortType[val].toLowerCase(), page: 1 });
|
||||||
this.state.page = 1;
|
|
||||||
this.setState(this.state);
|
|
||||||
this.updateUrl();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTypeChange(i: Search, event: any) {
|
handleTypeChange(i: Search, event: any) {
|
||||||
i.state.type_ = Number(event.target.value);
|
i.updateUrl({
|
||||||
i.state.page = 1;
|
type_: SearchType[Number(event.target.value)].toLowerCase(),
|
||||||
i.setState(i.state);
|
page: 1,
|
||||||
i.updateUrl();
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSearchSubmit(i: Search, event: any) {
|
handleSearchSubmit(i: Search, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.loading = true;
|
i.updateUrl({
|
||||||
i.search();
|
q: i.state.searchText,
|
||||||
i.setState(i.state);
|
type_: SearchType[i.state.type_].toLowerCase(),
|
||||||
i.updateUrl();
|
sort: SortType[i.state.sort].toLowerCase(),
|
||||||
|
page: i.state.page,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleQChange(i: Search, event: any) {
|
handleQChange(i: Search, event: any) {
|
||||||
i.state.q = event.target.value;
|
i.setState({ searchText: event.target.value });
|
||||||
i.setState(i.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUrl() {
|
updateUrl(paramUpdates: UrlParams) {
|
||||||
let typeStr = SearchType[this.state.type_].toLowerCase();
|
const qStr = paramUpdates.q || this.state.q;
|
||||||
let sortStr = SortType[this.state.sort].toLowerCase();
|
const typeStr =
|
||||||
|
paramUpdates.type_ || SearchType[this.state.type_].toLowerCase();
|
||||||
|
const sortStr =
|
||||||
|
paramUpdates.sort || SortType[this.state.sort].toLowerCase();
|
||||||
|
const page = paramUpdates.page || this.state.page;
|
||||||
this.props.history.push(
|
this.props.history.push(
|
||||||
`/search/q/${this.state.q}/type/${typeStr}/sort/${sortStr}/page/${this.state.page}`
|
`/search/q/${qStr}/type/${typeStr}/sort/${sortStr}/page/${page}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
ui/src/components/sort-select.tsx
vendored
10
ui/src/components/sort-select.tsx
vendored
|
@ -23,6 +23,12 @@ export class SortSelect extends Component<SortSelectProps, SortSelectState> {
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getDerivedStateFromProps(props: any): SortSelectState {
|
||||||
|
return {
|
||||||
|
sort: props.sort,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -59,8 +65,6 @@ export class SortSelect extends Component<SortSelectProps, SortSelectState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSortChange(i: SortSelect, event: any) {
|
handleSortChange(i: SortSelect, event: any) {
|
||||||
i.state.sort = Number(event.target.value);
|
i.props.onChange(event.target.value);
|
||||||
i.setState(i.state);
|
|
||||||
i.props.onChange(i.state.sort);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
307
ui/src/components/user-details.tsx
vendored
Normal file
307
ui/src/components/user-details.tsx
vendored
Normal file
|
@ -0,0 +1,307 @@
|
||||||
|
import { Component, linkEvent } from 'inferno';
|
||||||
|
import { WebSocketService, UserService } from '../services';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { retryWhen, delay, take, last } from 'rxjs/operators';
|
||||||
|
import { i18n } from '../i18next';
|
||||||
|
import {
|
||||||
|
UserOperation,
|
||||||
|
Post,
|
||||||
|
Comment,
|
||||||
|
CommunityUser,
|
||||||
|
SortType,
|
||||||
|
UserDetailsResponse,
|
||||||
|
UserView,
|
||||||
|
WebSocketJsonResponse,
|
||||||
|
UserDetailsView,
|
||||||
|
CommentResponse,
|
||||||
|
BanUserResponse,
|
||||||
|
PostResponse,
|
||||||
|
AddAdminResponse,
|
||||||
|
} from '../interfaces';
|
||||||
|
import {
|
||||||
|
wsJsonToRes,
|
||||||
|
toast,
|
||||||
|
commentsToFlatNodes,
|
||||||
|
setupTippy,
|
||||||
|
editCommentRes,
|
||||||
|
saveCommentRes,
|
||||||
|
createCommentLikeRes,
|
||||||
|
createPostLikeFindRes,
|
||||||
|
} from '../utils';
|
||||||
|
import { PostListing } from './post-listing';
|
||||||
|
import { CommentNodes } from './comment-nodes';
|
||||||
|
|
||||||
|
interface UserDetailsProps {
|
||||||
|
username?: string;
|
||||||
|
user_id?: number;
|
||||||
|
page: number;
|
||||||
|
limit: number;
|
||||||
|
sort: string;
|
||||||
|
enableDownvotes: boolean;
|
||||||
|
enableNsfw: boolean;
|
||||||
|
view: UserDetailsView;
|
||||||
|
onPageChange(page: number): number | any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserDetailsState {
|
||||||
|
follows: Array<CommunityUser>;
|
||||||
|
moderates: Array<CommunityUser>;
|
||||||
|
comments: Array<Comment>;
|
||||||
|
posts: Array<Post>;
|
||||||
|
saved?: Array<Post>;
|
||||||
|
admins: Array<UserView>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UserDetails extends Component<UserDetailsProps, UserDetailsState> {
|
||||||
|
private subscription: Subscription;
|
||||||
|
constructor(props: any, context: any) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
follows: [],
|
||||||
|
moderates: [],
|
||||||
|
comments: [],
|
||||||
|
posts: [],
|
||||||
|
saved: [],
|
||||||
|
admins: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
this.subscription = WebSocketService.Instance.subject
|
||||||
|
.pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
|
||||||
|
.subscribe(
|
||||||
|
msg => this.parseMessage(msg),
|
||||||
|
err => console.error(err),
|
||||||
|
() => console.log('complete')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.fetchUserData();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(lastProps: UserDetailsProps) {
|
||||||
|
for (const key of Object.keys(lastProps)) {
|
||||||
|
if (lastProps[key] !== this.props[key]) {
|
||||||
|
this.fetchUserData();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setupTippy();
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchUserData() {
|
||||||
|
WebSocketService.Instance.getUserDetails({
|
||||||
|
user_id: this.props.user_id,
|
||||||
|
username: this.props.username,
|
||||||
|
sort: this.props.sort,
|
||||||
|
saved_only: this.props.view === UserDetailsView.Saved,
|
||||||
|
page: this.props.page,
|
||||||
|
limit: this.props.limit,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{this.viewSelector(this.props.view)}
|
||||||
|
{this.paginator()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
viewSelector(view: UserDetailsView) {
|
||||||
|
if (view === UserDetailsView.Overview || view === UserDetailsView.Saved) {
|
||||||
|
return this.overview();
|
||||||
|
}
|
||||||
|
if (view === UserDetailsView.Comments) {
|
||||||
|
return this.comments();
|
||||||
|
}
|
||||||
|
if (view === UserDetailsView.Posts) {
|
||||||
|
return this.posts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
overview() {
|
||||||
|
const comments = this.state.comments.map((c: Comment) => {
|
||||||
|
return { type: 'comments', data: c };
|
||||||
|
});
|
||||||
|
const posts = this.state.posts.map((p: Post) => {
|
||||||
|
return { type: 'posts', data: p };
|
||||||
|
});
|
||||||
|
|
||||||
|
const combined: Array<{ type: string; data: Comment | Post }> = [
|
||||||
|
...comments,
|
||||||
|
...posts,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Sort it
|
||||||
|
if (SortType[this.props.sort] === SortType.New) {
|
||||||
|
combined.sort((a, b) => b.data.published.localeCompare(a.data.published));
|
||||||
|
} else {
|
||||||
|
combined.sort((a, b) => b.data.score - a.data.score);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{combined.map(i => (
|
||||||
|
<div>
|
||||||
|
{i.type === 'posts' ? (
|
||||||
|
<PostListing
|
||||||
|
post={i.data as Post}
|
||||||
|
admins={this.state.admins}
|
||||||
|
showCommunity
|
||||||
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
enableNsfw={this.props.enableNsfw}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<CommentNodes
|
||||||
|
nodes={[{ comment: i.data as Comment }]}
|
||||||
|
admins={this.state.admins}
|
||||||
|
noIndent
|
||||||
|
showContext
|
||||||
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
comments() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<CommentNodes
|
||||||
|
nodes={commentsToFlatNodes(this.state.comments)}
|
||||||
|
admins={this.state.admins}
|
||||||
|
noIndent
|
||||||
|
showContext
|
||||||
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
posts() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{this.state.posts.map(post => (
|
||||||
|
<PostListing
|
||||||
|
post={post}
|
||||||
|
admins={this.state.admins}
|
||||||
|
showCommunity
|
||||||
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
enableNsfw={this.props.enableNsfw}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
paginator() {
|
||||||
|
return (
|
||||||
|
<div class="my-2">
|
||||||
|
{this.props.page > 1 && (
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-secondary mr-1"
|
||||||
|
onClick={linkEvent(this, this.prevPage)}
|
||||||
|
>
|
||||||
|
{i18n.t('prev')}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{this.state.comments.length + this.state.posts.length > 0 && (
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-secondary"
|
||||||
|
onClick={linkEvent(this, this.nextPage)}
|
||||||
|
>
|
||||||
|
{i18n.t('next')}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
nextPage(i: UserDetails) {
|
||||||
|
i.props.onPageChange(i.props.page + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
prevPage(i: UserDetails) {
|
||||||
|
i.props.onPageChange(i.props.page - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
parseMessage(msg: WebSocketJsonResponse) {
|
||||||
|
const res = wsJsonToRes(msg);
|
||||||
|
|
||||||
|
if (msg.error) {
|
||||||
|
toast(i18n.t(msg.error), 'danger');
|
||||||
|
if (msg.error == 'couldnt_find_that_username_or_email') {
|
||||||
|
this.context.router.history.push('/');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (msg.reconnect) {
|
||||||
|
this.fetchUserData();
|
||||||
|
} else if (res.op == UserOperation.GetUserDetails) {
|
||||||
|
const data = res.data as UserDetailsResponse;
|
||||||
|
this.setState({
|
||||||
|
comments: data.comments,
|
||||||
|
follows: data.follows,
|
||||||
|
moderates: data.moderates,
|
||||||
|
posts: data.posts,
|
||||||
|
admins: data.admins,
|
||||||
|
});
|
||||||
|
} else if (res.op == UserOperation.CreateCommentLike) {
|
||||||
|
const data = res.data as CommentResponse;
|
||||||
|
createCommentLikeRes(data, this.state.comments);
|
||||||
|
this.setState({
|
||||||
|
comments: this.state.comments,
|
||||||
|
});
|
||||||
|
} else if (res.op == UserOperation.EditComment) {
|
||||||
|
const data = res.data as CommentResponse;
|
||||||
|
editCommentRes(data, this.state.comments);
|
||||||
|
this.setState({
|
||||||
|
comments: this.state.comments,
|
||||||
|
});
|
||||||
|
} else if (res.op == UserOperation.CreateComment) {
|
||||||
|
const data = res.data as CommentResponse;
|
||||||
|
if (
|
||||||
|
UserService.Instance.user &&
|
||||||
|
data.comment.creator_id == UserService.Instance.user.id
|
||||||
|
) {
|
||||||
|
toast(i18n.t('reply_sent'));
|
||||||
|
}
|
||||||
|
} else if (res.op == UserOperation.SaveComment) {
|
||||||
|
const data = res.data as CommentResponse;
|
||||||
|
saveCommentRes(data, this.state.comments);
|
||||||
|
this.setState({
|
||||||
|
comments: this.state.comments,
|
||||||
|
});
|
||||||
|
} else if (res.op == UserOperation.CreatePostLike) {
|
||||||
|
const data = res.data as PostResponse;
|
||||||
|
createPostLikeFindRes(data, this.state.posts);
|
||||||
|
this.setState({
|
||||||
|
posts: this.state.posts,
|
||||||
|
});
|
||||||
|
} else if (res.op == UserOperation.BanUser) {
|
||||||
|
const data = res.data as BanUserResponse;
|
||||||
|
this.state.comments
|
||||||
|
.filter(c => c.creator_id == data.user.id)
|
||||||
|
.forEach(c => (c.banned = data.banned));
|
||||||
|
this.state.posts
|
||||||
|
.filter(c => c.creator_id == data.user.id)
|
||||||
|
.forEach(c => (c.banned = data.banned));
|
||||||
|
this.setState({
|
||||||
|
posts: this.state.posts,
|
||||||
|
comments: this.state.comments,
|
||||||
|
});
|
||||||
|
} else if (res.op == UserOperation.AddAdmin) {
|
||||||
|
const data = res.data as AddAdminResponse;
|
||||||
|
this.setState({
|
||||||
|
admins: data.admins,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
381
ui/src/components/user.tsx
vendored
381
ui/src/components/user.tsx
vendored
|
@ -4,24 +4,18 @@ import { Subscription } from 'rxjs';
|
||||||
import { retryWhen, delay, take } from 'rxjs/operators';
|
import { retryWhen, delay, take } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
UserOperation,
|
UserOperation,
|
||||||
Post,
|
|
||||||
Comment,
|
|
||||||
CommunityUser,
|
CommunityUser,
|
||||||
GetUserDetailsForm,
|
|
||||||
SortType,
|
SortType,
|
||||||
ListingType,
|
ListingType,
|
||||||
UserDetailsResponse,
|
|
||||||
UserView,
|
UserView,
|
||||||
CommentResponse,
|
|
||||||
UserSettingsForm,
|
UserSettingsForm,
|
||||||
LoginResponse,
|
LoginResponse,
|
||||||
BanUserResponse,
|
|
||||||
AddAdminResponse,
|
|
||||||
DeleteAccountForm,
|
DeleteAccountForm,
|
||||||
PostResponse,
|
|
||||||
WebSocketJsonResponse,
|
WebSocketJsonResponse,
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
Site,
|
Site,
|
||||||
|
UserDetailsView,
|
||||||
|
UserDetailsResponse,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
import { WebSocketService, UserService } from '../services';
|
import { WebSocketService, UserService } from '../services';
|
||||||
import {
|
import {
|
||||||
|
@ -34,28 +28,15 @@ import {
|
||||||
languages,
|
languages,
|
||||||
showAvatars,
|
showAvatars,
|
||||||
toast,
|
toast,
|
||||||
editCommentRes,
|
|
||||||
saveCommentRes,
|
|
||||||
createCommentLikeRes,
|
|
||||||
createPostLikeFindRes,
|
|
||||||
commentsToFlatNodes,
|
|
||||||
setupTippy,
|
setupTippy,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { PostListing } from './post-listing';
|
|
||||||
import { UserListing } from './user-listing';
|
import { UserListing } from './user-listing';
|
||||||
import { SortSelect } from './sort-select';
|
import { SortSelect } from './sort-select';
|
||||||
import { ListingTypeSelect } from './listing-type-select';
|
import { ListingTypeSelect } from './listing-type-select';
|
||||||
import { CommentNodes } from './comment-nodes';
|
|
||||||
import { MomentTime } from './moment-time';
|
import { MomentTime } from './moment-time';
|
||||||
import { i18n } from '../i18next';
|
import { i18n } from '../i18next';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import { UserDetails } from './user-details';
|
||||||
enum View {
|
|
||||||
Overview,
|
|
||||||
Comments,
|
|
||||||
Posts,
|
|
||||||
Saved,
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UserState {
|
interface UserState {
|
||||||
user: UserView;
|
user: UserView;
|
||||||
|
@ -63,11 +44,7 @@ interface UserState {
|
||||||
username: string;
|
username: string;
|
||||||
follows: Array<CommunityUser>;
|
follows: Array<CommunityUser>;
|
||||||
moderates: Array<CommunityUser>;
|
moderates: Array<CommunityUser>;
|
||||||
comments: Array<Comment>;
|
view: UserDetailsView;
|
||||||
posts: Array<Post>;
|
|
||||||
saved?: Array<Post>;
|
|
||||||
admins: Array<UserView>;
|
|
||||||
view: View;
|
|
||||||
sort: SortType;
|
sort: SortType;
|
||||||
page: number;
|
page: number;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
@ -80,6 +57,20 @@ interface UserState {
|
||||||
site: Site;
|
site: Site;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface UserProps {
|
||||||
|
view: UserDetailsView;
|
||||||
|
sort: SortType;
|
||||||
|
page: number;
|
||||||
|
user_id: number | null;
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UrlParams {
|
||||||
|
view?: string;
|
||||||
|
sort?: string;
|
||||||
|
page?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class User extends Component<any, UserState> {
|
export class User extends Component<any, UserState> {
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: UserState = {
|
private emptyState: UserState = {
|
||||||
|
@ -102,14 +93,11 @@ export class User extends Component<any, UserState> {
|
||||||
username: null,
|
username: null,
|
||||||
follows: [],
|
follows: [],
|
||||||
moderates: [],
|
moderates: [],
|
||||||
comments: [],
|
loading: false,
|
||||||
posts: [],
|
|
||||||
admins: [],
|
|
||||||
loading: true,
|
|
||||||
avatarLoading: false,
|
avatarLoading: false,
|
||||||
view: this.getViewFromProps(this.props),
|
view: User.getViewFromProps(this.props.match.view),
|
||||||
sort: this.getSortTypeFromProps(this.props),
|
sort: User.getSortTypeFromProps(this.props.match.sort),
|
||||||
page: this.getPageFromProps(this.props),
|
page: User.getPageFromProps(this.props.match.page),
|
||||||
userSettingsForm: {
|
userSettingsForm: {
|
||||||
show_nsfw: null,
|
show_nsfw: null,
|
||||||
theme: null,
|
theme: null,
|
||||||
|
@ -153,8 +141,9 @@ export class User extends Component<any, UserState> {
|
||||||
this.handleUserSettingsListingTypeChange = this.handleUserSettingsListingTypeChange.bind(
|
this.handleUserSettingsListingTypeChange = this.handleUserSettingsListingTypeChange.bind(
|
||||||
this
|
this
|
||||||
);
|
);
|
||||||
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
|
|
||||||
this.state.user_id = Number(this.props.match.params.id);
|
this.state.user_id = Number(this.props.match.params.id) || null;
|
||||||
this.state.username = this.props.match.params.username;
|
this.state.username = this.props.match.params.username;
|
||||||
|
|
||||||
this.subscription = WebSocketService.Instance.subject
|
this.subscription = WebSocketService.Instance.subject
|
||||||
|
@ -165,7 +154,6 @@ export class User extends Component<any, UserState> {
|
||||||
() => console.log('complete')
|
() => console.log('complete')
|
||||||
);
|
);
|
||||||
|
|
||||||
this.refetch();
|
|
||||||
WebSocketService.Instance.getSite();
|
WebSocketService.Instance.getSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,38 +164,32 @@ export class User extends Component<any, UserState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getViewFromProps(props: any): View {
|
static getViewFromProps(view: any): UserDetailsView {
|
||||||
return props.match.params.view
|
return view
|
||||||
? View[capitalizeFirstLetter(props.match.params.view)]
|
? UserDetailsView[capitalizeFirstLetter(view)]
|
||||||
: View.Overview;
|
: UserDetailsView.Overview;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSortTypeFromProps(props: any): SortType {
|
static getSortTypeFromProps(sort: any): SortType {
|
||||||
return props.match.params.sort
|
return sort ? routeSortTypeToEnum(sort) : SortType.New;
|
||||||
? routeSortTypeToEnum(props.match.params.sort)
|
|
||||||
: SortType.New;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getPageFromProps(props: any): number {
|
static getPageFromProps(page: any): number {
|
||||||
return props.match.params.page ? Number(props.match.params.page) : 1;
|
return page ? Number(page) : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.subscription.unsubscribe();
|
this.subscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Necessary for back button for some reason
|
static getDerivedStateFromProps(props: any): UserProps {
|
||||||
componentWillReceiveProps(nextProps: any) {
|
return {
|
||||||
if (
|
view: this.getViewFromProps(props.match.params.view),
|
||||||
nextProps.history.action == 'POP' ||
|
sort: this.getSortTypeFromProps(props.match.params.sort),
|
||||||
nextProps.history.action == 'PUSH'
|
page: this.getPageFromProps(props.match.params.page),
|
||||||
) {
|
user_id: Number(props.match.params.id) || null,
|
||||||
this.state.view = this.getViewFromProps(nextProps);
|
username: props.match.params.username,
|
||||||
this.state.sort = this.getSortTypeFromProps(nextProps);
|
};
|
||||||
this.state.page = this.getPageFromProps(nextProps);
|
|
||||||
this.setState(this.state);
|
|
||||||
this.refetch();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(lastProps: any, _lastState: UserState, _snapshot: any) {
|
componentDidUpdate(lastProps: any, _lastState: UserState, _snapshot: any) {
|
||||||
|
@ -219,6 +201,8 @@ export class User extends Component<any, UserState> {
|
||||||
// Couldnt get a refresh working. This does for now.
|
// Couldnt get a refresh working. This does for now.
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
|
document.title = `/u/${this.state.username} - ${this.state.site.name}`;
|
||||||
|
setupTippy();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -242,14 +226,20 @@ export class User extends Component<any, UserState> {
|
||||||
class="rounded-circle mr-2"
|
class="rounded-circle mr-2"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<span>/u/{this.state.user.name}</span>
|
<span>/u/{this.state.username}</span>
|
||||||
</h5>
|
</h5>
|
||||||
{this.selects()}
|
{this.selects()}
|
||||||
{this.state.view == View.Overview && this.overview()}
|
<UserDetails
|
||||||
{this.state.view == View.Comments && this.comments()}
|
user_id={this.state.user_id}
|
||||||
{this.state.view == View.Posts && this.posts()}
|
username={this.state.username}
|
||||||
{this.state.view == View.Saved && this.overview()}
|
sort={SortType[this.state.sort]}
|
||||||
{this.paginator()}
|
page={this.state.page}
|
||||||
|
limit={fetchLimit}
|
||||||
|
enableDownvotes={this.state.site.enable_downvotes}
|
||||||
|
enableNsfw={this.state.site.enable_nsfw}
|
||||||
|
view={this.state.view}
|
||||||
|
onPageChange={this.handlePageChange}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-4">
|
<div class="col-12 col-md-4">
|
||||||
{this.userInfo()}
|
{this.userInfo()}
|
||||||
|
@ -268,52 +258,52 @@ export class User extends Component<any, UserState> {
|
||||||
<div class="btn-group btn-group-toggle">
|
<div class="btn-group btn-group-toggle">
|
||||||
<label
|
<label
|
||||||
className={`btn btn-sm btn-secondary pointer btn-outline-light
|
className={`btn btn-sm btn-secondary pointer btn-outline-light
|
||||||
${this.state.view == View.Overview && 'active'}
|
${this.state.view == UserDetailsView.Overview && 'active'}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
value={View.Overview}
|
value={UserDetailsView.Overview}
|
||||||
checked={this.state.view == View.Overview}
|
checked={this.state.view === UserDetailsView.Overview}
|
||||||
onChange={linkEvent(this, this.handleViewChange)}
|
onChange={linkEvent(this, this.handleViewChange)}
|
||||||
/>
|
/>
|
||||||
{i18n.t('overview')}
|
{i18n.t('overview')}
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
className={`btn btn-sm btn-secondary pointer btn-outline-light
|
className={`btn btn-sm btn-secondary pointer btn-outline-light
|
||||||
${this.state.view == View.Comments && 'active'}
|
${this.state.view == UserDetailsView.Comments && 'active'}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
value={View.Comments}
|
value={UserDetailsView.Comments}
|
||||||
checked={this.state.view == View.Comments}
|
checked={this.state.view == UserDetailsView.Comments}
|
||||||
onChange={linkEvent(this, this.handleViewChange)}
|
onChange={linkEvent(this, this.handleViewChange)}
|
||||||
/>
|
/>
|
||||||
{i18n.t('comments')}
|
{i18n.t('comments')}
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
className={`btn btn-sm btn-secondary pointer btn-outline-light
|
className={`btn btn-sm btn-secondary pointer btn-outline-light
|
||||||
${this.state.view == View.Posts && 'active'}
|
${this.state.view == UserDetailsView.Posts && 'active'}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
value={View.Posts}
|
value={UserDetailsView.Posts}
|
||||||
checked={this.state.view == View.Posts}
|
checked={this.state.view == UserDetailsView.Posts}
|
||||||
onChange={linkEvent(this, this.handleViewChange)}
|
onChange={linkEvent(this, this.handleViewChange)}
|
||||||
/>
|
/>
|
||||||
{i18n.t('posts')}
|
{i18n.t('posts')}
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
className={`btn btn-sm btn-secondary pointer btn-outline-light
|
className={`btn btn-sm btn-secondary pointer btn-outline-light
|
||||||
${this.state.view == View.Saved && 'active'}
|
${this.state.view == UserDetailsView.Saved && 'active'}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
value={View.Saved}
|
value={UserDetailsView.Saved}
|
||||||
checked={this.state.view == View.Saved}
|
checked={this.state.view == UserDetailsView.Saved}
|
||||||
onChange={linkEvent(this, this.handleViewChange)}
|
onChange={linkEvent(this, this.handleViewChange)}
|
||||||
/>
|
/>
|
||||||
{i18n.t('saved')}
|
{i18n.t('saved')}
|
||||||
|
@ -347,84 +337,6 @@ export class User extends Component<any, UserState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
overview() {
|
|
||||||
let combined: Array<{ type_: string; data: Comment | Post }> = [];
|
|
||||||
let comments = this.state.comments.map(e => {
|
|
||||||
return { type_: 'comments', data: e };
|
|
||||||
});
|
|
||||||
let posts = this.state.posts.map(e => {
|
|
||||||
return { type_: 'posts', data: e };
|
|
||||||
});
|
|
||||||
|
|
||||||
combined.push(...comments);
|
|
||||||
combined.push(...posts);
|
|
||||||
|
|
||||||
// Sort it
|
|
||||||
if (this.state.sort == SortType.New) {
|
|
||||||
combined.sort((a, b) => b.data.published.localeCompare(a.data.published));
|
|
||||||
} else {
|
|
||||||
combined.sort((a, b) => b.data.score - a.data.score);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{combined.map(i => (
|
|
||||||
<div>
|
|
||||||
{i.type_ == 'posts' ? (
|
|
||||||
<PostListing
|
|
||||||
post={i.data as Post}
|
|
||||||
admins={this.state.admins}
|
|
||||||
showCommunity
|
|
||||||
enableDownvotes={this.state.site.enable_downvotes}
|
|
||||||
enableNsfw={this.state.site.enable_nsfw}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<CommentNodes
|
|
||||||
nodes={[{ comment: i.data as Comment }]}
|
|
||||||
admins={this.state.admins}
|
|
||||||
noIndent
|
|
||||||
showCommunity
|
|
||||||
showContext
|
|
||||||
enableDownvotes={this.state.site.enable_downvotes}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
comments() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<CommentNodes
|
|
||||||
nodes={commentsToFlatNodes(this.state.comments)}
|
|
||||||
admins={this.state.admins}
|
|
||||||
noIndent
|
|
||||||
showCommunity
|
|
||||||
showContext
|
|
||||||
enableDownvotes={this.state.site.enable_downvotes}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
posts() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{this.state.posts.map(post => (
|
|
||||||
<PostListing
|
|
||||||
post={post}
|
|
||||||
admins={this.state.admins}
|
|
||||||
showCommunity
|
|
||||||
enableDownvotes={this.state.site.enable_downvotes}
|
|
||||||
enableNsfw={this.state.site.enable_nsfw}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
userInfo() {
|
userInfo() {
|
||||||
let user = this.state.user;
|
let user = this.state.user;
|
||||||
return (
|
return (
|
||||||
|
@ -896,77 +808,30 @@ export class User extends Component<any, UserState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
paginator() {
|
updateUrl(paramUpdates: UrlParams) {
|
||||||
return (
|
const page = paramUpdates.page || this.state.page;
|
||||||
<div class="my-2">
|
const viewStr =
|
||||||
{this.state.page > 1 && (
|
paramUpdates.view || UserDetailsView[this.state.view].toLowerCase();
|
||||||
<button
|
const sortStr =
|
||||||
class="btn btn-sm btn-secondary mr-1"
|
paramUpdates.sort || SortType[this.state.sort].toLowerCase();
|
||||||
onClick={linkEvent(this, this.prevPage)}
|
|
||||||
>
|
|
||||||
{i18n.t('prev')}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{this.state.comments.length + this.state.posts.length > 0 && (
|
|
||||||
<button
|
|
||||||
class="btn btn-sm btn-secondary"
|
|
||||||
onClick={linkEvent(this, this.nextPage)}
|
|
||||||
>
|
|
||||||
{i18n.t('next')}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateUrl() {
|
|
||||||
let viewStr = View[this.state.view].toLowerCase();
|
|
||||||
let sortStr = SortType[this.state.sort].toLowerCase();
|
|
||||||
this.props.history.push(
|
this.props.history.push(
|
||||||
`/u/${this.state.user.name}/view/${viewStr}/sort/${sortStr}/page/${this.state.page}`
|
`/u/${this.state.username}/view/${viewStr}/sort/${sortStr}/page/${page}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
nextPage(i: User) {
|
handlePageChange(page: number) {
|
||||||
i.state.page++;
|
this.updateUrl({ page });
|
||||||
i.setState(i.state);
|
|
||||||
i.updateUrl();
|
|
||||||
i.refetch();
|
|
||||||
}
|
|
||||||
|
|
||||||
prevPage(i: User) {
|
|
||||||
i.state.page--;
|
|
||||||
i.setState(i.state);
|
|
||||||
i.updateUrl();
|
|
||||||
i.refetch();
|
|
||||||
}
|
|
||||||
|
|
||||||
refetch() {
|
|
||||||
let form: GetUserDetailsForm = {
|
|
||||||
user_id: this.state.user_id,
|
|
||||||
username: this.state.username,
|
|
||||||
sort: SortType[this.state.sort],
|
|
||||||
saved_only: this.state.view == View.Saved,
|
|
||||||
page: this.state.page,
|
|
||||||
limit: fetchLimit,
|
|
||||||
};
|
|
||||||
WebSocketService.Instance.getUserDetails(form);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSortChange(val: SortType) {
|
handleSortChange(val: SortType) {
|
||||||
this.state.sort = val;
|
this.updateUrl({ sort: SortType[val].toLowerCase(), page: 1 });
|
||||||
this.state.page = 1;
|
|
||||||
this.setState(this.state);
|
|
||||||
this.updateUrl();
|
|
||||||
this.refetch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleViewChange(i: User, event: any) {
|
handleViewChange(i: User, event: any) {
|
||||||
i.state.view = Number(event.target.value);
|
i.updateUrl({
|
||||||
i.state.page = 1;
|
view: UserDetailsView[Number(event.target.value)].toLowerCase(),
|
||||||
i.setState(i.state);
|
page: 1,
|
||||||
i.updateUrl();
|
});
|
||||||
i.refetch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUserSettingsShowNsfwChange(i: User, event: any) {
|
handleUserSettingsShowNsfwChange(i: User, event: any) {
|
||||||
|
@ -1136,29 +1001,28 @@ export class User extends Component<any, UserState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: WebSocketJsonResponse) {
|
parseMessage(msg: WebSocketJsonResponse) {
|
||||||
console.log(msg);
|
const res = wsJsonToRes(msg);
|
||||||
let res = wsJsonToRes(msg);
|
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
toast(i18n.t(msg.error), 'danger');
|
toast(i18n.t(msg.error), 'danger');
|
||||||
this.state.deleteAccountLoading = false;
|
|
||||||
this.state.avatarLoading = false;
|
|
||||||
this.state.userSettingsLoading = false;
|
|
||||||
if (msg.error == 'couldnt_find_that_username_or_email') {
|
if (msg.error == 'couldnt_find_that_username_or_email') {
|
||||||
this.context.router.history.push('/');
|
this.context.router.history.push('/');
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState({
|
||||||
|
deleteAccountLoading: false,
|
||||||
|
avatarLoading: false,
|
||||||
|
userSettingsLoading: false,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
} else if (msg.reconnect) {
|
|
||||||
this.refetch();
|
|
||||||
} else if (res.op == UserOperation.GetUserDetails) {
|
} else if (res.op == UserOperation.GetUserDetails) {
|
||||||
let data = res.data as UserDetailsResponse;
|
// Since the UserDetails contains posts/comments as well as some general user info we listen here as well
|
||||||
|
// and set the parent state if it is not set or differs
|
||||||
|
const data = res.data as UserDetailsResponse;
|
||||||
|
|
||||||
|
if (this.state.user.id !== data.user.id) {
|
||||||
this.state.user = data.user;
|
this.state.user = data.user;
|
||||||
this.state.comments = data.comments;
|
|
||||||
this.state.follows = data.follows;
|
this.state.follows = data.follows;
|
||||||
this.state.moderates = data.moderates;
|
this.state.moderates = data.moderates;
|
||||||
this.state.posts = data.posts;
|
|
||||||
this.state.admins = data.admins;
|
|
||||||
this.state.loading = false;
|
|
||||||
if (this.isCurrentUser) {
|
if (this.isCurrentUser) {
|
||||||
this.state.userSettingsForm.show_nsfw =
|
this.state.userSettingsForm.show_nsfw =
|
||||||
UserService.Instance.user.show_nsfw;
|
UserService.Instance.user.show_nsfw;
|
||||||
|
@ -1177,61 +1041,26 @@ export class User extends Component<any, UserState> {
|
||||||
UserService.Instance.user.show_avatars;
|
UserService.Instance.user.show_avatars;
|
||||||
this.state.userSettingsForm.matrix_user_id = this.state.user.matrix_user_id;
|
this.state.userSettingsForm.matrix_user_id = this.state.user.matrix_user_id;
|
||||||
}
|
}
|
||||||
document.title = `/u/${this.state.user.name} - ${this.state.site.name}`;
|
|
||||||
window.scrollTo(0, 0);
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
setupTippy();
|
|
||||||
} else if (res.op == UserOperation.EditComment) {
|
|
||||||
let data = res.data as CommentResponse;
|
|
||||||
editCommentRes(data, this.state.comments);
|
|
||||||
this.setState(this.state);
|
|
||||||
} else if (res.op == UserOperation.CreateComment) {
|
|
||||||
let data = res.data as CommentResponse;
|
|
||||||
if (
|
|
||||||
UserService.Instance.user &&
|
|
||||||
data.comment.creator_id == UserService.Instance.user.id
|
|
||||||
) {
|
|
||||||
toast(i18n.t('reply_sent'));
|
|
||||||
}
|
}
|
||||||
} else if (res.op == UserOperation.SaveComment) {
|
|
||||||
let data = res.data as CommentResponse;
|
|
||||||
saveCommentRes(data, this.state.comments);
|
|
||||||
this.setState(this.state);
|
|
||||||
} else if (res.op == UserOperation.CreateCommentLike) {
|
|
||||||
let data = res.data as CommentResponse;
|
|
||||||
createCommentLikeRes(data, this.state.comments);
|
|
||||||
this.setState(this.state);
|
|
||||||
} else if (res.op == UserOperation.CreatePostLike) {
|
|
||||||
let data = res.data as PostResponse;
|
|
||||||
createPostLikeFindRes(data, this.state.posts);
|
|
||||||
this.setState(this.state);
|
|
||||||
} else if (res.op == UserOperation.BanUser) {
|
|
||||||
let data = res.data as BanUserResponse;
|
|
||||||
this.state.comments
|
|
||||||
.filter(c => c.creator_id == data.user.id)
|
|
||||||
.forEach(c => (c.banned = data.banned));
|
|
||||||
this.state.posts
|
|
||||||
.filter(c => c.creator_id == data.user.id)
|
|
||||||
.forEach(c => (c.banned = data.banned));
|
|
||||||
this.setState(this.state);
|
|
||||||
} else if (res.op == UserOperation.AddAdmin) {
|
|
||||||
let data = res.data as AddAdminResponse;
|
|
||||||
this.state.admins = data.admins;
|
|
||||||
this.setState(this.state);
|
|
||||||
} else if (res.op == UserOperation.SaveUserSettings) {
|
} else if (res.op == UserOperation.SaveUserSettings) {
|
||||||
let data = res.data as LoginResponse;
|
const data = res.data as LoginResponse;
|
||||||
this.state.userSettingsLoading = false;
|
|
||||||
this.setState(this.state);
|
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
|
this.setState({
|
||||||
|
userSettingsLoading: false,
|
||||||
|
});
|
||||||
|
window.scrollTo(0, 0);
|
||||||
} else if (res.op == UserOperation.DeleteAccount) {
|
} else if (res.op == UserOperation.DeleteAccount) {
|
||||||
this.state.deleteAccountLoading = false;
|
this.setState({
|
||||||
this.state.deleteAccountShowConfirm = false;
|
deleteAccountLoading: false,
|
||||||
this.setState(this.state);
|
deleteAccountShowConfirm: false,
|
||||||
|
});
|
||||||
this.context.router.history.push('/');
|
this.context.router.history.push('/');
|
||||||
} else if (res.op == UserOperation.GetSite) {
|
} else if (res.op == UserOperation.GetSite) {
|
||||||
let data = res.data as GetSiteResponse;
|
const data = res.data as GetSiteResponse;
|
||||||
this.state.site = data.site;
|
this.setState({
|
||||||
this.setState(this.state);
|
site: data.site,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
7
ui/src/interfaces.ts
vendored
7
ui/src/interfaces.ts
vendored
|
@ -937,3 +937,10 @@ export interface WebSocketJsonResponse {
|
||||||
error?: string;
|
error?: string;
|
||||||
reconnect?: boolean;
|
reconnect?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum UserDetailsView {
|
||||||
|
Overview,
|
||||||
|
Comments,
|
||||||
|
Posts,
|
||||||
|
Saved,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue