diff options
author | Felix Pojtinger <felix@pojtinger.com> | 2019-05-04 14:23:37 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-04 14:23:37 +0200 |
commit | 45dd106c13f3976fadab4fdb1e53b6794776303f (patch) | |
tree | 77ba95dbdd02babfdb75667e6e4d4bd9980d31a6 /ui | |
parent | 76e3833f49e40dd38b8ece549b5aefeeb893b0da (diff) | |
parent | 9d3b4de4362e39b05ef759372aecf66da2b14c3b (diff) |
Merge branch 'master' into master
Diffstat (limited to 'ui')
33 files changed, 662 insertions, 323 deletions
@@ -45,7 +45,7 @@ Sparky.task('config', _ => { // Sparky.task('version', _ => setVersion()); Sparky.task('clean', _ => Sparky.src('dist/').clean('dist/')); Sparky.task('env', _ => (isProduction = true)); -Sparky.task('copy-assets', () => Sparky.src('assets/*.svg').dest('dist/')); +Sparky.task('copy-assets', () => Sparky.src('assets/*.*').dest('dist/')); Sparky.task('dev', ['clean', 'config', 'copy-assets'], _ => { fuse.dev(); app.hmr().watch(); diff --git a/ui/package.json b/ui/package.json index b5bb14ef..d806575c 100644 --- a/ui/package.json +++ b/ui/package.json @@ -19,6 +19,7 @@ "@types/js-cookie": "^2.2.1", "@types/jwt-decode": "^2.2.1", "@types/markdown-it": "^0.0.7", + "@types/markdown-it-container": "^2.0.2", "autosize": "^4.0.2", "classcat": "^1.1.3", "dotenv": "^6.1.0", @@ -27,6 +28,7 @@ "js-cookie": "^2.2.0", "jwt-decode": "^2.2.0", "markdown-it": "^8.4.2", + "markdown-it-container": "^2.0.0", "moment": "^2.24.0", "rxjs": "^6.4.0" }, diff --git a/ui/src/components/comment-form.tsx b/ui/src/components/comment-form.tsx index 1b4eda99..a69ae06f 100644 --- a/ui/src/components/comment-form.tsx +++ b/ui/src/components/comment-form.tsx @@ -56,7 +56,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> { <form onSubmit={linkEvent(this, this.handleCommentSubmit)}> <div class="form-group row"> <div class="col-sm-12"> - <textarea class="form-control" value={this.state.commentForm.content} onInput={linkEvent(this, this.handleCommentContentChange)} required disabled={this.props.disabled} rows={2} /> + <textarea class="form-control" value={this.state.commentForm.content} onInput={linkEvent(this, this.handleCommentContentChange)} required disabled={this.props.disabled} rows={2} maxLength={10000} /> </div> </div> <div class="row"> diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index 0c0fd821..92eda3ad 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -92,7 +92,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { {this.state.showEdit && <CommentForm node={node} edit onReplyCancel={this.handleReplyCancel} disabled={this.props.locked} />} {!this.state.showEdit && <div> - <div className="md-div" dangerouslySetInnerHTML={mdToHtml(node.comment.removed ? '*removed*' : node.comment.content)} /> + <div className="md-div" dangerouslySetInnerHTML={mdToHtml(node.comment.removed ? '*removed*' : node.comment.deleted ? '*deleted*' : node.comment.content)} /> <ul class="list-inline mb-1 text-muted small font-weight-bold"> {UserService.Instance.user && !this.props.viewOnly && <> @@ -108,7 +108,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { <span class="pointer" onClick={linkEvent(this, this.handleEditClick)}>edit</span> </li> <li className="list-inline-item"> - <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}>delete</span> + <span class="pointer" onClick={linkEvent(this, this.handleDeleteClick)}> + {!this.props.node.comment.deleted ? 'delete' : 'restore'} + </span> </li> </> } @@ -252,11 +254,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { handleDeleteClick(i: CommentNode) { let deleteForm: CommentFormI = { - content: '*deleted*', + content: i.props.node.comment.content, edit_id: i.props.node.comment.id, creator_id: i.props.node.comment.creator_id, post_id: i.props.node.comment.post_id, parent_id: i.props.node.comment.parent_id, + deleted: !i.props.node.comment.deleted, auth: null }; WebSocketService.Instance.editComment(deleteForm); diff --git a/ui/src/components/communities.tsx b/ui/src/components/communities.tsx index b5233c22..190f8e3d 100644 --- a/ui/src/components/communities.tsx +++ b/ui/src/components/communities.tsx @@ -10,6 +10,7 @@ declare const Sortable: any; interface CommunitiesState { communities: Array<Community>; + page: number; loading: boolean; } @@ -17,7 +18,8 @@ export class Communities extends Component<any, CommunitiesState> { private subscription: Subscription; private emptyState: CommunitiesState = { communities: [], - loading: true + loading: true, + page: this.getPageFromProps(this.props), } constructor(props: any, context: any) { @@ -31,13 +33,12 @@ export class Communities extends Component<any, CommunitiesState> { () => console.log('complete') ); - let listCommunitiesForm: ListCommunitiesForm = { - sort: SortType[SortType.TopAll], - limit: 100, - } + this.refetch(); - WebSocketService.Instance.listCommunities(listCommunitiesForm); + } + getPageFromProps(props: any): number { + return (props.match.params.page) ? Number(props.match.params.page) : 1; } componentWillUnmount() { @@ -45,40 +46,49 @@ export class Communities extends Component<any, CommunitiesState> { } componentDidMount() { - document.title = "Forums - Lemmy"; + document.title = "Communities - Lemmy"; let table = document.querySelector('#community_table'); Sortable.initTable(table); } + // Necessary for back button for some reason + componentWillReceiveProps(nextProps: any) { + if (nextProps.history.action == 'POP') { + this.state = this.emptyState; + this.state.page = this.getPageFromProps(nextProps); + this.refetch(); + } + } + render() { return ( <div class="container"> {this.state.loading ? <h5 class=""><svg class="icon icon-spinner spin"><use xlinkHref="#icon-spinner"></use></svg></h5> : <div> - <h5>Forums</h5> + <h5>List of communities</h5> <div class="table-responsive"> <table id="community_table" class="table table-sm table-hover"> <thead class="pointer"> <tr> <th>Name</th> - <th>Title</th> + <th class="d-none d-lg-table-cell">Title</th> <th>Category</th> - <th class="text-right d-none d-md-table-cell">Subscribers</th> - <th class="text-right d-none d-md-table-cell">Posts</th> - <th class="text-right d-none d-md-table-cell">Comments</th> + <th class="text-right">Subscribers</th> + <th class="text-right d-none d-lg-table-cell">Posts</th> + <th class="text-right d-none d-lg-table-cell">Comments</th> <th></th> </tr> </thead> <tbody> {this.state.communities.map(community => <tr> - <td><Link to={`/f/${community.name}`}>{community.name}</Link></td> - <td>{community.title}</td> + <td><Link to={`/c/${community.name}`}>{community.name}</Link></td> + <td class="d-none d-lg-table-cell">{community.title}</td> <td>{community.category_name}</td> - <td class="text-right d-none d-md-table-cell">{community.number_of_subscribers}</td> - <td class="text-right d-none d-md-table-cell">{community.number_of_posts}</td> - <td class="text-right d-none d-md-table-cell">{community.number_of_comments}</td> + <td class="text-right">{community.number_of_subscribers}</td> + <td class="text-right d-none d-lg-table-cell">{community.number_of_posts}</td> + <td class="text-right d-none d-lg-table-cell">{community.number_of_comments}</td> <td class="text-right"> {community.subscribed ? <span class="pointer btn-link" onClick={linkEvent(community.id, this.handleUnsubscribe)}>Unsubscribe</span> : @@ -90,12 +100,42 @@ export class Communities extends Component<any, CommunitiesState> { </tbody> </table> </div> + {this.paginator()} </div> } </div> ); } + paginator() { + return ( + <div class="mt-2"> + {this.state.page > 1 && + <button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}>Prev</button> + } + <button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}>Next</button> + </div> + ); + } + + updateUrl() { + this.props.history.push(`/communities/page/${this.state.page}`); + } + + nextPage(i: Communities) { + i.state.page++; + i.setState(i.state); + i.updateUrl(); + i.refetch(); + } + + prevPage(i: Communities) { + i.state.page--; + i.setState(i.state); + i.updateUrl(); + i.refetch(); + } + handleUnsubscribe(communityId: number) { let form: FollowCommunityForm = { community_id: communityId, @@ -112,6 +152,17 @@ export class Communities extends Component<any, CommunitiesState> { WebSocketService.Instance.followCommunity(form); } + refetch() { + let listCommunitiesForm: ListCommunitiesForm = { + sort: SortType[SortType.TopAll], + limit: 100, + page: this.state.page, + } + + WebSocketService.Instance.listCommunities(listCommunitiesForm); + + } + parseMessage(msg: any) { console.log(msg); let op: UserOperation = msgOp(msg); diff --git a/ui/src/components/community-form.tsx b/ui/src/components/community-form.tsx index 5effa73a..e295dcbe 100644 --- a/ui/src/components/community-form.tsx +++ b/ui/src/components/community-form.tsx @@ -88,7 +88,7 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt <div class="form-group row"> <label class="col-12 col-form-label">Sidebar</label> <div class="col-12"> - <textarea value={this.state.communityForm.description} onInput={linkEvent(this, this.handleCommunityDescriptionChange)} class="form-control" rows={3} /> + <textarea value={this.state.communityForm.description} onInput={linkEvent(this, this.handleCommunityDescriptionChange)} class="form-control" rows={3} maxLength={10000} /> </div> </div> <div class="form-group row"> @@ -120,10 +120,7 @@ export class CommunityForm extends Component<CommunityFormProps, CommunityFormSt if (i.props.community) { WebSocketService.Instance.editCommunity(i.state.communityForm); } else { - - setTimeout(function(){ - WebSocketService.Instance.createCommunity(i.state.communityForm); - }, 10000); + WebSocketService.Instance.createCommunity(i.state.communityForm); } i.setState(i.state); } diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index ba542582..93cdbd92 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -1,11 +1,11 @@ -import { Component } from 'inferno'; +import { Component, linkEvent } from 'inferno'; import { Subscription } from "rxjs"; import { retryWhen, delay, take } from 'rxjs/operators'; -import { UserOperation, Community as CommunityI, GetCommunityResponse, CommunityResponse, CommunityUser, UserView } from '../interfaces'; +import { UserOperation, Community as CommunityI, GetCommunityResponse, CommunityResponse, CommunityUser, UserView, SortType, Post, GetPostsForm, ListingType, GetPostsResponse, CreatePostLikeResponse } from '../interfaces'; import { WebSocketService } from '../services'; import { PostListings } from './post-listings'; import { Sidebar } from './sidebar'; -import { msgOp } from '../utils'; +import { msgOp, routeSortTypeToEnum, fetchLimit } from '../utils'; interface State { community: CommunityI; @@ -14,6 +14,9 @@ interface State { moderators: Array<CommunityUser>; admins: Array<UserView>; loading: boolean; + posts: Array<Post>; + sort: SortType; + page: number; } export class Community extends Component<any, State> { @@ -38,7 +41,20 @@ export class Community extends Component<any, State> { admins: [], communityId: Number(this.props.match.params.id), communityName: this.props.match.params.name, - loading: true + loading: true, + posts: [], + sort: this.getSortTypeFromProps(this.props), + page: this.getPageFromProps(this.props), + } + + getSortTypeFromProps(props: any): SortType { + return (props.match.params.sort) ? + routeSortTypeToEnum(props.match.params.sort) : + SortType.Hot; + } + + getPageFromProps(props: any): number { + return (props.match.params.page) ? Number(props.match.params.page) : 1; } constructor(props: any, context: any) { @@ -66,6 +82,16 @@ export class Community extends Component<any, State> { this.subscription.unsubscribe(); } + // Necessary for back button for some reason + componentWillReceiveProps(nextProps: any) { + if (nextProps.history.action == 'POP') { + this.state = this.emptyState; + this.state.sort = this.getSortTypeFromProps(nextProps); + this.state.page = this.getPageFromProps(nextProps); + this.fetchPosts(); + } + } + render() { return ( <div class="container"> @@ -78,7 +104,9 @@ export class Community extends Component<any, State> { <small className="ml-2 text-muted font-italic">removed</small> } </h5> - {this.state.community && <PostListings communityId={this.state.community.id} />} + {this.selects()} + <PostListings posts={this.state.posts} /> + {this.paginator()} </div> <div class="col-12 col-md-3"> <Sidebar @@ -93,6 +121,72 @@ export class Community extends Component<any, State> { ) } + selects() { + return ( + <div className="mb-2"> + <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto"> + <option disabled>Sort Type</option> + <option value={SortType.Hot}>Hot</option> + <option value={SortType.New}>New</option> + <option disabled>──────────</option> + <option value={SortType.TopDay}>Top Day</option> + <option value={SortType.TopWeek}>Week</option> + <option value={SortType.TopMonth}>Month</option> + <option value={SortType.TopYear}>Year</option> + <option value={SortType.TopAll}>All</option> + </select> + </div> + ) + } + + paginator() { + return ( + <div class="mt-2"> + {this.state.page > 1 && + <button class="btn btn-sm btn-secondary mr-1" onClick={linkEvent(this, this.prevPage)}>Prev</button> + } + <button class="btn btn-sm btn-secondary" onClick={linkEvent(this, this.nextPage)}>Next</button> + </div> + ); + } + + nextPage(i: Community) { + i.state.page++; + i.setState(i.state); + i.updateUrl(); + i.fetchPosts(); + } + + prevPage(i: Community) { + i.state.page--; + i.setState(i.state); + i.updateUrl(); + i.fetchPosts(); + } + + handleSortChange(i: Community, event: any) { + i.state.sort = Number(event.target.value); + i.state.page = 1; + i.setState(i.state); + i.updateUrl(); + i.fetchPosts(); + } + + updateUrl() { + let sortStr = SortType[this.state.sort].toLowerCase(); + this.props.history.push(`/c/${this.state.community.name}/sort/${sortStr}/page/${this.state.page}`); + } + + fetchPosts() { + let getPostsForm: GetPostsForm = { + page: this.state.page, + limit: fetchLimit, + sort: SortType[this.state.sort], + type_: ListingType[ListingType.Community], + community_id: this.state.community.id, + } + WebSocketService.Instance.getPosts(getPostsForm); + } parseMessage(msg: any) { console.log(msg); @@ -105,9 +199,9 @@ export class Community extends Component<any, State> { this.state.community = res.community; this.state.moderators = res.moderators; this.state.admins = res.admins; - this.state.loading = false; - document.title = `/f/${this.state.community.name} - Lemmy`; + document.title = `/c/${this.state.community.name} - Lemmy`; this.setState(this.state); + this.fetchPosts(); } else if (op == UserOperation.EditCommunity) { let res: CommunityResponse = msg; this.state.community = res.community; @@ -117,6 +211,19 @@ export class Community extends Component<any, State> { this.state.community.subscribed = res.community.subscribed; this.state.community.number_of_subscribers = res.community.number_of_subscribers; this.setState(this.state); + } else if (op == UserOperation.GetPosts) { + let res: GetPostsResponse = msg; + this.state.posts = res.posts; + this.state.loading = false; + this.setState(this.state); + } else if (op == UserOperation.CreatePostLike) { + let res: CreatePostLikeResponse = msg; + let found = this.state.posts.find(c => c.id == res.post.id); + found.my_vote = res.post.my_vote; + found.score = res.post.score; + found.upvotes = res.post.upvotes; + found.downvotes = res.post.downvotes; + this.setState(this.state); } } } diff --git a/ui/src/components/create-community.tsx b/ui/src/components/create-community.tsx index 04ef6d3b..a9345f72 100644 --- a/ui/src/components/create-community.tsx +++ b/ui/src/components/create-community.tsx @@ -10,7 +10,7 @@ export class CreateCommunity extends Component<any, any> { } componentDidMount() { - document.title = "Create Forum - Lemmy"; + document.title = "Create Community - Lemmy"; } render() { @@ -18,7 +18,7 @@ export class CreateCommunity extends Component<any, any> { <div class="container"> <div class="row"> <div class="col-12 col-lg-6 mb-4"> - <h5>Create Forum</h5> + <h5>Create Community</h5> <CommunityForm onCreate={this.handleCommunityCreate}/> </div> </div> @@ -27,7 +27,7 @@ export class CreateCommunity extends Component<any, any> { } handleCommunityCreate(community: Community) { - this.props.history.push(`/f/${community.name}`); + this.props.history.push(`/c/${community.name}`); } } diff --git a/ui/src/components/create-post.tsx b/ui/src/components/create-post.tsx index e2998ca7..1958be72 100644 --- a/ui/src/components/create-post.tsx +++ b/ui/src/components/create-post.tsx @@ -18,13 +18,23 @@ export class CreatePost extends Component<any, any> { <div class="row"> <div class="col-12 col-lg-6 mb-4"> <h5>Create a Post</h5> - <PostForm onCreate={this.handlePostCreate}/> + <PostForm onCreate={this.handlePostCreate} prevCommunityName={this.prevCommunityName} /> </div> </div> </div> ) } + get prevCommunityName(): string { + if (this.props.location.state) { + let lastLocation = this.props.location.state.prevPath; + if (lastLocation.includes("/c/")) { + return lastLocation.split("/c/")[1]; + } + } + return undefined; + } + handlePostCreate(id: number) { this.props.history.push(`/post/${id}`); } diff --git a/ui/src/components/home.tsx b/ui/src/components/home.tsx index cebe222b..e69de29b 100644 --- a/ui/src/components/home.tsx +++ b/ui/src/components/home.tsx @@ -1,24 +0,0 @@ -import { Component } from 'inferno'; -import { Main } from './main'; -import { ListingType } from '../interfaces'; - -export class Home extends Component<any, any> { - - constructor(props: any, context: any) { - super(props, context); - } - - render() { - return ( - <Main type={this.listType()}/> - ) - } - - componentDidMount() { - document.title = "Lemmy"; - } - - listType(): ListingType { - return (this.props.match.path == '/all') ? ListingType.All : ListingType.Subscribed; - } -} diff --git a/ui/src/components/inbox.tsx b/ui/src/components/inbox.tsx index 02d813f3..659a4a20 100644 --- a/ui/src/components/inbox.tsx +++ b/ui/src/components/inbox.tsx @@ -58,7 +58,16 @@ export class Inbox extends Component<any, InboxState> { <div class="container"> <div class="row"> <div class="col-12"> - <h5>Inbox for <Link to={`/u/${user.username}`}>{user.username}</Link></h5> + <h5 class="mb-0"> + <span>Inbox for <Link to={`/u/${user.username}`}>{user.username}</Link></span> + </h5> + {this.state.replies.length > 0 && this.state.unreadType == UnreadType.Unread && + <ul class="list-inline mb-1 text-muted small font-weight-bold"> + <li className="list-inline-item"> + <span class="pointer" onClick={this.markAllAsRead}>mark all as read</span> + </li> + </ul> + } {this.selects()} {this.replies()} {this.paginator()} @@ -71,12 +80,12 @@ export class Inbox extends Component<any, InboxState> { selects() { return ( <div className="mb-2"> - <select value={this.state.unreadType} onChange={linkEvent(this, this.handleUnreadTypeChange)} class="custom-select w-auto"> + <select value={this.state.unreadType} onChange={linkEvent(this, this.handleUnreadTypeChange)} class="custom-select custom-select-sm w-auto"> <option disabled>Type</option> <option value={UnreadType.Unread}>Unread</option> <option value={UnreadType.All}>All</option> </select> - <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select w-auto ml-2"> + <select value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select custom-select-sm w-auto ml-2"> <option disabled>Sort Type</option> <option value={SortType.New}>New</option> <option value={SortType.TopDay}>Top Day</option> @@ -147,13 +156,17 @@ export class Inbox extends Component<any, InboxState> { i.refetch(); } + markAllAsRead() { + WebSocketService.Instance.markAllAsRead(); + } + parseMessage(msg: any) { console.log(msg); let op: UserOperation = msgOp(msg); if (msg.error) { alert(msg.error); return; - } else if (op == UserOperation.GetReplies) { + } else if (op == UserOperation.GetReplies || op == UserOperation.MarkAllAsRead) { let res: GetRepliesResponse = msg; this.state.replies = res.replies; this.sendRepliesCount(); @@ -165,6 +178,7 @@ export class Inbox extends Component<any, InboxState> { found.content = res.comment.content; found.updated = res.comment.updated; found.removed = res.comment.removed; + found.deleted = res.comment.deleted; found.upvotes = res.comment.upvotes; found.downvotes = res.comment.downvotes; found.score = res.comment.score; diff --git a/ui/src/components/login.tsx b/ui/src/components/login.tsx index 5c0b8dd1..b2ad70a1 100644 --- a/ui/src/components/login.tsx +++ b/ui/src/components/login.tsx @@ -95,7 +95,7 @@ export class Login extends Component<any, State> { </div> </div> </form> - Forgot your password or deleted your account? Reset your password. TODO + {/* Forgot your password or deleted your account? Reset your password. TODO */} </div> ); } @@ -161,7 +161,6 @@ export class Login extends Component<any, State> { event.preventDefault(); i.state.registerLoading = true; i.setState(i.state); - event.preventDefault(); let endTimer = new Date().getTime(); let elapsed = endTimer - i.state.registerForm.spam_timeri; @@ -209,14 +208,14 @@ export class Login extends Component<any, State> { return; } else { if (op == UserOperation.Login) { - this.state.loginLoading = false; - this.state.registerLoading = false; + this.state = this.emptyState; + this.setState(this.state); let res: LoginResponse = msg; UserService.Instance.login(res); this.props.history.push('/'); } else if (op == UserOperation.Register) { - this.state.loginLoading = false; - this.state.registerLoading = false; + this.state = this.emptyState; + this.setState(this.state); let res: LoginResponse = msg; UserService.Instance.login(res); this.props.history.push('/communities'); diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index 6911ca40..cd679ecb 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -2,16 +2,11 @@ import { Component, linkEvent } from 'inferno'; import { Link } from 'inferno-router'; import { Subscription } from "rxjs"; import { retryWhen, delay, take } from 'rxjs/operators'; -import { UserOperation, CommunityUser, GetFollowedCommunitiesResponse, ListCommunitiesForm, ListCommunitiesResponse, Community, SortType, GetSiteResponse, ListingType, SiteResponse } from '../interfaces'; +import { UserOperation, CommunityUser, GetFollowedCommunitiesResponse, ListCommunitiesForm, ListCommunitiesResponse, Community, SortType, GetSiteResponse, ListingType, SiteResponse, GetPostsResponse, CreatePostLikeResponse, Post, GetPostsForm } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { PostListings } from './post-listings'; import { SiteForm } from './site-form'; -import { msgOp, repoUrl, mdToHtml } from '../utils'; - - -interface MainProps { - type: ListingType; -} +import { msgOp, repoUrl, mdToHtml, fetchLimit, routeSortTypeToEnum, routeListingTypeToEnum } from '../utils'; interface MainState { subscribedCommunities: Array<CommunityUser>; @@ -19,9 +14,13 @@ interface MainState { site: GetSiteResponse; showEditSite: boolean; loading: boolean; + posts: Array<Post>; + type_: ListingType; + sort: SortType; + page: number; } -export class Main extends Component<MainProps, MainState> { +export class Main extends Component<any, MainState> { private subscription: Subscription; private emptyState: MainState = { @@ -43,7 +42,29 @@ export class Main extends Component<MainProps, MainState> { banned: [], }, showEditSite: false, - loading: true + loading: true, + posts: [], + type_: this.getListingTypeFromProps(this.props), + |