From a5aeba3ad02c4a56649fe346df67f9079686c9c0 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Mon, 7 Sep 2020 17:24:48 -0500 Subject: [PATCH] Create community, Create post, and instances pages done. --- src/shared/components/communities.tsx | 5 +- src/shared/components/community-form.tsx | 17 +-- src/shared/components/create-community.tsx | 99 +++++++------ src/shared/components/create-post.tsx | 115 ++++++++++------ src/shared/components/instances.tsx | 95 +++---------- src/shared/components/markdown-textarea.tsx | 9 +- src/shared/components/post-form.tsx | 145 +++++++++----------- src/shared/routes.ts | 13 +- src/shared/utils.ts | 134 +++++++++--------- 9 files changed, 308 insertions(+), 324 deletions(-) diff --git a/src/shared/components/communities.tsx b/src/shared/components/communities.tsx index a6b97264..d04020d4 100644 --- a/src/shared/components/communities.tsx +++ b/src/shared/components/communities.tsx @@ -52,13 +52,16 @@ export class Communities extends Component { constructor(props: any, context: any) { super(props, context); this.state = this.emptyState; - this.parseMessage = this.parseMessage.bind(this); + this.parseMessage = this.parseMessage.bind(this); this.subscription = wsSubscribe(this.parseMessage); // Only fetch the data if coming from another route if (this.isoData.path == this.context.router.route.match.url) { this.state.communities = this.isoData.routeData[0].communities; + this.state.communities.sort( + (a, b) => b.number_of_subscribers - a.number_of_subscribers + ); this.state.loading = false; } else { this.refetch(); diff --git a/src/shared/components/community-form.tsx b/src/shared/components/community-form.tsx index 6fa72ec1..301a9eb1 100644 --- a/src/shared/components/community-form.tsx +++ b/src/shared/components/community-form.tsx @@ -6,7 +6,6 @@ import { CommunityForm as CommunityFormI, UserOperation, Category, - ListCategoriesResponse, CommunityResponse, WebSocketJsonResponse, Community, @@ -20,6 +19,7 @@ import { ImageUploadForm } from './image-upload-form'; interface CommunityFormProps { community?: Community; // If a community is given, that means this is an edit + categories: Category[]; onCancel?(): any; onCreate?(community: Community): any; onEdit?(community: Community): any; @@ -28,7 +28,6 @@ interface CommunityFormProps { interface CommunityFormState { communityForm: CommunityFormI; - categories: Category[]; loading: boolean; } @@ -43,12 +42,11 @@ export class CommunityForm extends Component< communityForm: { name: null, title: null, - category_id: null, + category_id: this.props.categories[0].id, nsfw: false, icon: null, banner: null, }, - categories: [], loading: false, }; @@ -88,8 +86,6 @@ export class CommunityForm extends Component< err => console.error(err), () => console.log('complete') ); - - WebSocketService.Instance.listCategories(); } componentDidUpdate() { @@ -218,7 +214,7 @@ export class CommunityForm extends Component< value={this.state.communityForm.category_id} onInput={linkEvent(this, this.handleCommunityCategoryChange)} > - {this.state.categories.map(category => ( + {this.props.categories.map(category => ( ))} @@ -344,13 +340,6 @@ export class CommunityForm extends Component< this.state.loading = false; this.setState(this.state); return; - } else if (res.op == UserOperation.ListCategories) { - let data = res.data as ListCategoriesResponse; - this.state.categories = data.categories; - if (!this.props.community) { - this.state.communityForm.category_id = data.categories[0].id; - } - this.setState(this.state); } else if (res.op == UserOperation.CreateCommunity) { let data = res.data as CommunityResponse; this.state.loading = false; diff --git a/src/shared/components/create-community.tsx b/src/shared/components/create-community.tsx index 6f156211..07680b5d 100644 --- a/src/shared/components/create-community.tsx +++ b/src/shared/components/create-community.tsx @@ -1,87 +1,95 @@ import { Component } from 'inferno'; import { Helmet } from 'inferno-helmet'; import { Subscription } from 'rxjs'; -import { retryWhen, delay, take } from 'rxjs/operators'; import { CommunityForm } from './community-form'; import { Community, UserOperation, WebSocketJsonResponse, - GetSiteResponse, Site, + ListCategoriesResponse, + Category, } from 'lemmy-js-client'; -import { toast, wsJsonToRes } from '../utils'; +import { + setIsoData, + toast, + wsJsonToRes, + wsSubscribe, + isBrowser, + lemmyHttp, +} from '../utils'; import { WebSocketService, UserService } from '../services'; import { i18n } from '../i18next'; interface CreateCommunityState { site: Site; + categories: Category[]; + loading: boolean; } export class CreateCommunity extends Component { + private isoData = setIsoData(this.context); private subscription: Subscription; private emptyState: CreateCommunityState = { - site: { - id: undefined, - name: undefined, - creator_id: undefined, - published: undefined, - creator_name: undefined, - number_of_users: undefined, - number_of_posts: undefined, - number_of_comments: undefined, - number_of_communities: undefined, - enable_downvotes: undefined, - open_registration: undefined, - enable_nsfw: undefined, - }, + site: this.isoData.site.site, + categories: [], + loading: true, }; constructor(props: any, context: any) { super(props, context); this.handleCommunityCreate = this.handleCommunityCreate.bind(this); this.state = this.emptyState; + this.parseMessage = this.parseMessage.bind(this); + this.subscription = wsSubscribe(this.parseMessage); + + // TODO not sure if this works if (!UserService.Instance.user) { toast(i18n.t('not_logged_in'), 'danger'); this.context.router.history.push(`/login`); } - 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') - ); - - WebSocketService.Instance.getSite(); + // Only fetch the data if coming from another route + if (this.isoData.path == this.context.router.route.match.url) { + this.state.categories = this.isoData.routeData[0].categories; + this.state.loading = false; + } else { + WebSocketService.Instance.listCategories(); + } } componentWillUnmount() { - this.subscription.unsubscribe(); + if (isBrowser()) { + this.subscription.unsubscribe(); + } } get documentTitle(): string { - if (this.state.site.name) { - return `${i18n.t('create_community')} - ${this.state.site.name}`; - } else { - return 'Lemmy'; - } + return `${i18n.t('create_community')} - ${this.state.site.name}`; } render() { return (
-
-
-
{i18n.t('create_community')}
- + {this.state.loading ? ( +
+ + + +
+ ) : ( +
+
+
{i18n.t('create_community')}
+ +
-
+ )}
); } @@ -90,15 +98,20 @@ export class CreateCommunity extends Component { this.props.history.push(`/c/${community.name}`); } + static fetchInitialData(_auth: string, _path: string): Promise[] { + return [lemmyHttp.listCategories()]; + } + parseMessage(msg: WebSocketJsonResponse) { console.log(msg); let res = wsJsonToRes(msg); if (msg.error) { // Toast errors are already handled by community-form return; - } else if (res.op == UserOperation.GetSite) { - let data = res.data as GetSiteResponse; - this.state.site = data.site; + } else if (res.op == UserOperation.ListCategories) { + let data = res.data as ListCategoriesResponse; + this.state.categories = data.categories; + this.state.loading = false; this.setState(this.state); } } diff --git a/src/shared/components/create-post.tsx b/src/shared/components/create-post.tsx index f4c03b65..fc759d0f 100644 --- a/src/shared/components/create-post.tsx +++ b/src/shared/components/create-post.tsx @@ -1,40 +1,41 @@ import { Component } from 'inferno'; import { Helmet } from 'inferno-helmet'; import { Subscription } from 'rxjs'; -import { retryWhen, delay, take } from 'rxjs/operators'; import { PostForm } from './post-form'; -import { toast, wsJsonToRes } from '../utils'; -import { WebSocketService, UserService } from '../services'; +import { + lemmyHttp, + setAuth, + setIsoData, + toast, + wsJsonToRes, + wsSubscribe, +} from '../utils'; +import { UserService, WebSocketService } from '../services'; import { UserOperation, PostFormParams, WebSocketJsonResponse, - GetSiteResponse, + ListCommunitiesResponse, + Community, Site, + ListCommunitiesForm, + SortType, } from 'lemmy-js-client'; import { i18n } from '../i18next'; interface CreatePostState { site: Site; + communities: Community[]; + loading: boolean; } export class CreatePost extends Component { + private isoData = setIsoData(this.context); private subscription: Subscription; private emptyState: CreatePostState = { - site: { - id: undefined, - name: undefined, - creator_id: undefined, - published: undefined, - creator_name: undefined, - number_of_users: undefined, - number_of_posts: undefined, - number_of_comments: undefined, - number_of_communities: undefined, - enable_downvotes: undefined, - open_registration: undefined, - enable_nsfw: undefined, - }, + site: this.isoData.site.site, + communities: [], + loading: true, }; constructor(props: any, context: any) { @@ -47,15 +48,24 @@ export class CreatePost extends Component { this.context.router.history.push(`/login`); } - 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') - ); + this.parseMessage = this.parseMessage.bind(this); + this.subscription = wsSubscribe(this.parseMessage); - WebSocketService.Instance.getSite(); + // Only fetch the data if coming from another route + if (this.isoData.path == this.context.router.route.match.url) { + this.state.communities = this.isoData.routeData[0].communities; + this.state.loading = false; + } else { + this.refetch(); + } + } + + refetch() { + let listCommunitiesForm: ListCommunitiesForm = { + sort: SortType.TopAll, + limit: 9999, + }; + WebSocketService.Instance.listCommunities(listCommunitiesForm); } componentWillUnmount() { @@ -63,28 +73,33 @@ export class CreatePost extends Component { } get documentTitle(): string { - if (this.state.site.name) { - return `${i18n.t('create_post')} - ${this.state.site.name}`; - } else { - return 'Lemmy'; - } + return `${i18n.t('create_post')} - ${this.state.site.name}`; } render() { return (
-
-
-
{i18n.t('create_post')}
- + {this.state.loading ? ( +
+ + + +
+ ) : ( +
+
+
{i18n.t('create_post')}
+ +
-
+ )}
); } @@ -110,22 +125,32 @@ export class CreatePost extends Component { return lastLocation.split('/c/')[1]; } } - return; + return null; } handlePostCreate(id: number) { this.props.history.push(`/post/${id}`); } + static fetchInitialData(auth: string, _path: string): Promise[] { + let listCommunitiesForm: ListCommunitiesForm = { + sort: SortType.TopAll, + limit: 9999, + }; + setAuth(listCommunitiesForm, auth); + return [lemmyHttp.listCommunities(listCommunitiesForm)]; + } + parseMessage(msg: WebSocketJsonResponse) { console.log(msg); let res = wsJsonToRes(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); return; - } else if (res.op == UserOperation.GetSite) { - let data = res.data as GetSiteResponse; - this.state.site = data.site; + } else if (res.op == UserOperation.ListCommunities) { + let data = res.data as ListCommunitiesResponse; + this.state.communities = data.communities; + this.state.loading = false; this.setState(this.state); } } diff --git a/src/shared/components/instances.tsx b/src/shared/components/instances.tsx index c54b3718..0929761d 100644 --- a/src/shared/components/instances.tsx +++ b/src/shared/components/instances.tsx @@ -1,101 +1,50 @@ import { Component } from 'inferno'; import { Helmet } from 'inferno-helmet'; -import { Subscription } from 'rxjs'; -import { retryWhen, delay, take } from 'rxjs/operators'; -import { - UserOperation, - WebSocketJsonResponse, - GetSiteResponse, -} from 'lemmy-js-client'; -import { WebSocketService } from '../services'; -import { wsJsonToRes, toast, isBrowser } from '../utils'; +import { GetSiteResponse } from 'lemmy-js-client'; +import { setIsoData } from '../utils'; import { i18n } from '../i18next'; interface InstancesState { - loading: boolean; siteRes: GetSiteResponse; } export class Instances extends Component { - private subscription: Subscription; + private isoData = setIsoData(this.context); private emptyState: InstancesState = { - loading: true, - siteRes: undefined, + siteRes: this.isoData.site, }; constructor(props: any, context: any) { super(props, context); this.state = this.emptyState; - - if (isBrowser()) { - 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') - ); - - WebSocketService.Instance.getSite(); - } - } - - componentWillUnmount() { - this.subscription.unsubscribe(); } get documentTitle(): string { - if (this.state.siteRes) { - return `${i18n.t('instances')} - ${this.state.siteRes.site.name}`; - } else { - return 'Lemmy'; - } + return `${i18n.t('instances')} - ${this.state.siteRes.site.name}`; } render() { return (
- {this.state.loading ? ( -
- - - -
- ) : ( -
-
{i18n.t('linked_instances')}
- {this.state.siteRes && - this.state.siteRes.federated_instances.length ? ( -
    - {this.state.siteRes.federated_instances.map(i => ( -
  • - - {i} - -
  • - ))} -
- ) : ( -
{i18n.t('none_found')}
- )} -
- )} +
+
{i18n.t('linked_instances')}
+ {this.state.siteRes && + this.state.siteRes.federated_instances.length ? ( +
    + {this.state.siteRes.federated_instances.map(i => ( +
  • + + {i} + +
  • + ))} +
+ ) : ( +
{i18n.t('none_found')}
+ )} +
); } - - parseMessage(msg: WebSocketJsonResponse) { - console.log(msg); - let res = wsJsonToRes(msg); - if (msg.error) { - toast(i18n.t(msg.error), 'danger'); - return; - } else if (res.op == UserOperation.GetSite) { - let data = res.data as GetSiteResponse; - this.state.siteRes = data; - this.state.loading = false; - this.setState(this.state); - } - } } diff --git a/src/shared/components/markdown-textarea.tsx b/src/shared/components/markdown-textarea.tsx index 1faa8a9d..c566c2ef 100644 --- a/src/shared/components/markdown-textarea.tsx +++ b/src/shared/components/markdown-textarea.tsx @@ -8,10 +8,10 @@ import { setupTribute, pictrsDeleteToast, setupTippy, + isBrowser, } from '../utils'; import { UserService } from '../services'; import autosize from 'autosize'; -import Tribute from 'tributejs/src/Tribute.js'; import { i18n } from '../i18next'; interface MarkdownTextAreaProps { @@ -41,7 +41,7 @@ export class MarkdownTextArea extends Component< > { private id = `comment-textarea-${randomStr()}`; private formId = `comment-form-${randomStr()}`; - private tribute: Tribute; + private tribute: any; private emptyState: MarkdownTextAreaState = { content: this.props.initialContent, previewMode: false, @@ -52,8 +52,9 @@ export class MarkdownTextArea extends Component< constructor(props: any, context: any) { super(props, context); - // TODO - /* this.tribute = setupTribute(); */ + if (isBrowser()) { + this.tribute = setupTribute(); + } this.state = this.emptyState; } diff --git a/src/shared/components/post-form.tsx b/src/shared/components/post-form.tsx index 8e34c6d8..ac6ad3b0 100644 --- a/src/shared/components/post-form.tsx +++ b/src/shared/components/post-form.tsx @@ -3,7 +3,6 @@ import { Prompt } from 'inferno-router'; import { PostListings } from './post-listings'; import { MarkdownTextArea } from './markdown-textarea'; import { Subscription } from 'rxjs'; -import { retryWhen, delay, take } from 'rxjs/operators'; import { PostForm as PostFormI, PostFormParams, @@ -11,8 +10,6 @@ import { PostResponse, UserOperation, Community, - ListCommunitiesResponse, - ListCommunitiesForm, SortType, SearchForm, SearchType, @@ -34,14 +31,22 @@ import { hostname, pictrsDeleteToast, validTitle, + wsSubscribe, + isBrowser, } from '../utils'; -import Choices from 'choices.js'; + +var Choices; +if (isBrowser()) { + Choices = require('choices.js'); +} + import { i18n } from '../i18next'; const MAX_POST_TITLE_LENGTH = 200; interface PostFormProps { post?: Post; // If a post is given, that means this is an edit + communities: Community[]; params?: PostFormParams; onCancel?(): any; onCreate?(id: number): any; @@ -52,7 +57,6 @@ interface PostFormProps { interface PostFormState { postForm: PostFormI; - communities: Community[]; loading: boolean; imageLoading: boolean; previewMode: boolean; @@ -64,7 +68,7 @@ interface PostFormState { export class PostForm extends Component { private id = `post-form-${randomStr()}`; private subscription: Subscription; - private choices: Choices; + private choices: any; private emptyState: PostFormState = { postForm: { name: null, @@ -72,7 +76,6 @@ export class PostForm extends Component { auth: null, community_id: null, }, - communities: [], loading: false, imageLoading: false, previewMode: false, @@ -112,24 +115,12 @@ export class PostForm extends Component { } } - 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') - ); - - let listCommunitiesForm: ListCommunitiesForm = { - sort: SortType.TopAll, - limit: 9999, - }; - - WebSocketService.Instance.listCommunities(listCommunitiesForm); + this.subscription = wsSubscribe(this.parseMessage); } componentDidMount() { setupTippy(); + this.setupCommunities(); } componentDidUpdate() { @@ -209,7 +200,7 @@ export class PostForm extends Component { onChange={linkEvent(this, this.handleImageUpload)} /> - {validURL(this.state.postForm.url) && ( + {this.state.postForm.url && validURL(this.state.postForm.url) && ( { onInput={linkEvent(this, this.handlePostCommunityChange)} > - {this.state.communities.map(community => ( + {this.props.communities.map(community => (