import { Component, linkEvent } from 'inferno';
-import { Link } from 'inferno-router';
+import { Helmet } from 'inferno-helmet';
import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators';
import {
WebSocketJsonResponse,
GetSiteResponse,
Site,
-} from '../interfaces';
+} from 'lemmy-js-client';
import { WebSocketService } from '../services';
import {
wsJsonToRes,
createCommentLikeRes,
createPostLikeFindRes,
commentsToFlatNodes,
+ getPageFromProps,
} from '../utils';
import { PostListing } from './post-listing';
import { UserListing } from './user-listing';
searchText: string;
}
+interface SearchProps {
+ q: string;
+ type_: SearchType;
+ sort: SortType;
+ page: number;
+}
+
+interface UrlParams {
+ q?: string;
+ type_?: SearchType;
+ sort?: SortType;
+ page?: number;
+}
+
export class Search extends Component<any, SearchState> {
private subscription: Subscription;
private emptyState: SearchState = {
q: Search.getSearchQueryFromProps(this.props),
type_: Search.getSearchTypeFromProps(this.props),
sort: Search.getSortTypeFromProps(this.props),
- page: Search.getPageFromProps(this.props),
+ page: getPageFromProps(this.props),
searchText: Search.getSearchQueryFromProps(this.props),
searchResponse: {
type_: null,
: SortType.TopAll;
}
- static getPageFromProps(props: any): number {
- return props.match.params.page ? Number(props.match.params.page) : 1;
- }
-
constructor(props: any, context: any) {
super(props, context);
this.subscription.unsubscribe();
}
- static getDerivedStateFromProps(props) {
+ static getDerivedStateFromProps(props: any): SearchProps {
return {
q: Search.getSearchQueryFromProps(props),
type_: Search.getSearchTypeFromProps(props),
sort: Search.getSortTypeFromProps(props),
- page: Search.getPageFromProps(props),
+ page: getPageFromProps(props),
};
}
- componentDidUpdate(_, lastState) {
+ componentDidUpdate(_: any, lastState: SearchState) {
if (
lastState.q !== this.state.q ||
lastState.type_ !== this.state.type_ ||
}
}
+ get documentTitle(): string {
+ if (this.state.site.name) {
+ if (this.state.q) {
+ return `${i18n.t('search')} - ${this.state.q} - ${
+ this.state.site.name
+ }`;
+ } else {
+ return `${i18n.t('search')} - ${this.state.site.name}`;
+ }
+ } else {
+ return 'Lemmy';
+ }
+ }
+
render() {
return (
<div class="container">
+ <Helmet title={this.documentTitle} />
<h5>{i18n.t('search')}</h5>
{this.selects()}
{this.searchForm()}
>
<input
type="text"
- class="form-control mr-2"
+ class="form-control mr-2 mb-2"
value={this.state.searchText}
placeholder={`${i18n.t('search')}...`}
onInput={linkEvent(this, this.handleQChange)}
required
minLength={3}
/>
- <button type="submit" class="btn btn-secondary mr-2">
+ <button type="submit" class="btn btn-secondary mr-2 mb-2">
{this.state.loading ? (
<svg class="icon icon-spinner spin">
<use xlinkHref="#icon-spinner"></use>
<select
value={this.state.type_}
onChange={linkEvent(this, this.handleTypeChange)}
- class="custom-select custom-select-sm w-auto"
+ class="custom-select w-auto mb-2"
>
<option disabled>{i18n.t('type')}</option>
<option value={SearchType.All}>{i18n.t('all')}</option>
<div class="col-12">
{i.type_ == 'posts' && (
<PostListing
+ key={(i.data as Post).id}
post={i.data as Post}
showCommunity
enableDownvotes={this.state.site.enable_downvotes}
)}
{i.type_ == 'comments' && (
<CommentNodes
+ key={(i.data as Comment).id}
nodes={[{ comment: i.data as Comment }]}
locked
noIndent
{i.type_ == 'users' && (
<div>
<span>
- @
<UserListing
user={{
name: (i.data as UserView).name,
+ preferred_username: (i.data as UserView)
+ .preferred_username,
avatar: (i.data as UserView).avatar,
}}
/>
<div class="row">
<div class="col-12">
<span>
- @
<UserListing
user={{
name: user.name,
avatar: user.avatar,
+ preferred_username: user.preferred_username,
}}
/>
</span>
<div class="mt-2">
{this.state.page > 1 && (
<button
- class="btn btn-sm btn-secondary mr-1"
+ class="btn btn-secondary mr-1"
onClick={linkEvent(this, this.prevPage)}
>
{i18n.t('prev')}
{this.resultsCount() > 0 && (
<button
- class="btn btn-sm btn-secondary"
+ class="btn btn-secondary"
onClick={linkEvent(this, this.nextPage)}
>
{i18n.t('next')}
search() {
let form: SearchForm = {
q: this.state.q,
- type_: SearchType[this.state.type_],
- sort: SortType[this.state.sort],
+ type_: this.state.type_,
+ sort: this.state.sort,
page: this.state.page,
limit: fetchLimit,
};
}
handleTypeChange(i: Search, event: any) {
- i.updateUrl({ type_: Number(event.target.value), page: 1 });
+ i.updateUrl({
+ type_: SearchType[event.target.value],
+ page: 1,
+ });
}
handleSearchSubmit(i: Search, event: any) {
i.setState({ searchText: event.target.value });
}
- updateUrl(paramUpdates: {
- q?: string;
- type_?: number;
- sort?: SortType;
- page?: number;
- }) {
- const qStr = paramUpdates.q || Search.getSearchQueryFromProps(this.props);
- const typeStr =
- SearchType[paramUpdates.type_] ||
- SearchType[Search.getSearchTypeFromProps(this.props)];
- const sortStr =
- SortType[paramUpdates.sort] ||
- SortType[Search.getSortTypeFromProps(this.props)];
- const page = paramUpdates.page || Search.getPageFromProps(this.props);
+ updateUrl(paramUpdates: UrlParams) {
+ const qStr = paramUpdates.q || this.state.q;
+ const typeStr = paramUpdates.type_ || this.state.type_;
+ const sortStr = paramUpdates.sort || this.state.sort;
+ const page = paramUpdates.page || this.state.page;
this.props.history.push(
- `/search/q/${qStr}/type/${typeStr.toLowerCase()}/sort/${sortStr.toLowerCase()}/page/${page}`
+ `/search/q/${qStr}/type/${typeStr}/sort/${sortStr}/page/${page}`
);
}
let data = res.data as SearchResponse;
this.state.searchResponse = data;
this.state.loading = false;
- document.title = `${i18n.t('search')} - ${this.state.q} - ${
- this.state.site.name
- }`;
window.scrollTo(0, 0);
this.setState(this.state);
} else if (res.op == UserOperation.CreateCommentLike) {
let data = res.data as GetSiteResponse;
this.state.site = data.site;
this.setState(this.state);
- document.title = `${i18n.t('search')} - ${data.site.name}`;
}
}
}