1 import { Component, linkEvent } from 'inferno';
2 import { Prompt } from 'inferno-router';
3 import { Subscription } from 'rxjs';
4 import { retryWhen, delay, take } from 'rxjs/operators';
6 CommunityForm as CommunityFormI,
9 ListCategoriesResponse,
11 WebSocketJsonResponse,
12 } from '../interfaces';
13 import { WebSocketService } from '../services';
14 import { wsJsonToRes, capitalizeFirstLetter, toast, randomStr } from '../utils';
15 import { i18n } from '../i18next';
17 import { Community } from '../interfaces';
18 import { MarkdownTextArea } from './markdown-textarea';
19 import { ImageUploadForm } from './image-upload-form';
21 interface CommunityFormProps {
22 community?: Community; // If a community is given, that means this is an edit
24 onCreate?(community: Community): any;
25 onEdit?(community: Community): any;
29 interface CommunityFormState {
30 communityForm: CommunityFormI;
31 categories: Array<Category>;
35 export class CommunityForm extends Component<
39 private id = `community-form-${randomStr()}`;
40 private subscription: Subscription;
42 private emptyState: CommunityFormState = {
55 constructor(props: any, context: any) {
56 super(props, context);
58 this.state = this.emptyState;
60 this.handleCommunityDescriptionChange = this.handleCommunityDescriptionChange.bind(
64 this.handleIconUpload = this.handleIconUpload.bind(this);
65 this.handleIconRemove = this.handleIconRemove.bind(this);
67 this.handleBannerUpload = this.handleBannerUpload.bind(this);
68 this.handleBannerRemove = this.handleBannerRemove.bind(this);
70 if (this.props.community) {
71 this.state.communityForm = {
72 name: this.props.community.name,
73 title: this.props.community.title,
74 category_id: this.props.community.category_id,
75 description: this.props.community.description,
76 edit_id: this.props.community.id,
77 nsfw: this.props.community.nsfw,
78 icon: this.props.community.icon,
79 banner: this.props.community.banner,
84 this.subscription = WebSocketService.Instance.subject
85 .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
87 msg => this.parseMessage(msg),
88 err => console.error(err),
89 () => console.log('complete')
92 WebSocketService.Instance.listCategories();
95 componentDidUpdate() {
97 !this.state.loading &&
98 (this.state.communityForm.name ||
99 this.state.communityForm.title ||
100 this.state.communityForm.description)
102 window.onbeforeunload = () => true;
104 window.onbeforeunload = undefined;
108 componentWillUnmount() {
109 this.subscription.unsubscribe();
110 window.onbeforeunload = null;
118 !this.state.loading &&
119 (this.state.communityForm.name ||
120 this.state.communityForm.title ||
121 this.state.communityForm.description)
123 message={i18n.t('block_leaving')}
125 <form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
126 {!this.props.community && (
127 <div class="form-group row">
128 <label class="col-12 col-form-label" htmlFor="community-name">
131 class="pointer unselectable ml-2 text-muted"
132 data-tippy-content={i18n.t('name_explain')}
134 <svg class="icon icon-inline">
135 <use xlinkHref="#icon-help-circle"></use>
144 value={this.state.communityForm.name}
145 onInput={linkEvent(this, this.handleCommunityNameChange)}
150 title={i18n.t('community_reqs')}
155 <div class="form-group row">
156 <label class="col-12 col-form-label" htmlFor="community-title">
157 {i18n.t('display_name')}
159 class="pointer unselectable ml-2 text-muted"
160 data-tippy-content={i18n.t('display_name_explain')}
162 <svg class="icon icon-inline">
163 <use xlinkHref="#icon-help-circle"></use>
171 value={this.state.communityForm.title}
172 onInput={linkEvent(this, this.handleCommunityTitleChange)}
180 <div class="form-group">
181 <label>{i18n.t('icon')}</label>
183 uploadTitle={i18n.t('upload_icon')}
184 imageSrc={this.state.communityForm.icon}
185 onUpload={this.handleIconUpload}
186 onRemove={this.handleIconRemove}
190 <div class="form-group">
191 <label>{i18n.t('banner')}</label>
193 uploadTitle={i18n.t('upload_banner')}
194 imageSrc={this.state.communityForm.banner}
195 onUpload={this.handleBannerUpload}
196 onRemove={this.handleBannerRemove}
199 <div class="form-group row">
200 <label class="col-12 col-form-label" htmlFor={this.id}>
205 initialContent={this.state.communityForm.description}
206 onContentChange={this.handleCommunityDescriptionChange}
210 <div class="form-group row">
211 <label class="col-12 col-form-label" htmlFor="community-category">
217 id="community-category"
218 value={this.state.communityForm.category_id}
219 onInput={linkEvent(this, this.handleCommunityCategoryChange)}
221 {this.state.categories.map(category => (
222 <option value={category.id}>{category.name}</option>
228 {this.props.enableNsfw && (
229 <div class="form-group row">
231 <div class="form-check">
233 class="form-check-input"
236 checked={this.state.communityForm.nsfw}
237 onChange={linkEvent(this, this.handleCommunityNsfwChange)}
239 <label class="form-check-label" htmlFor="community-nsfw">
246 <div class="form-group row">
250 class="btn btn-secondary mr-2"
251 disabled={this.state.loading}
253 {this.state.loading ? (
254 <svg class="icon icon-spinner spin">
255 <use xlinkHref="#icon-spinner"></use>
257 ) : this.props.community ? (
258 capitalizeFirstLetter(i18n.t('save'))
260 capitalizeFirstLetter(i18n.t('create'))
263 {this.props.community && (
266 class="btn btn-secondary"
267 onClick={linkEvent(this, this.handleCancel)}
279 handleCreateCommunitySubmit(i: CommunityForm, event: any) {
280 event.preventDefault();
281 i.state.loading = true;
282 if (i.props.community) {
283 WebSocketService.Instance.editCommunity(i.state.communityForm);
285 WebSocketService.Instance.createCommunity(i.state.communityForm);
290 handleCommunityNameChange(i: CommunityForm, event: any) {
291 i.state.communityForm.name = event.target.value;
295 handleCommunityTitleChange(i: CommunityForm, event: any) {
296 i.state.communityForm.title = event.target.value;
300 handleCommunityDescriptionChange(val: string) {
301 this.state.communityForm.description = val;
302 this.setState(this.state);
305 handleCommunityCategoryChange(i: CommunityForm, event: any) {
306 i.state.communityForm.category_id = Number(event.target.value);
310 handleCommunityNsfwChange(i: CommunityForm, event: any) {
311 i.state.communityForm.nsfw = event.target.checked;
315 handleCancel(i: CommunityForm) {
319 handleIconUpload(url: string) {
320 this.state.communityForm.icon = url;
321 this.setState(this.state);
325 this.state.communityForm.icon = '';
326 this.setState(this.state);
329 handleBannerUpload(url: string) {
330 this.state.communityForm.banner = url;
331 this.setState(this.state);
334 handleBannerRemove() {
335 this.state.communityForm.banner = '';
336 this.setState(this.state);
339 parseMessage(msg: WebSocketJsonResponse) {
340 let res = wsJsonToRes(msg);
343 toast(i18n.t(msg.error), 'danger');
344 this.state.loading = false;
345 this.setState(this.state);
347 } else if (res.op == UserOperation.ListCategories) {
348 let data = res.data as ListCategoriesResponse;
349 this.state.categories = data.categories;
350 if (!this.props.community) {
351 this.state.communityForm.category_id = data.categories[0].id;
353 this.setState(this.state);
354 } else if (res.op == UserOperation.CreateCommunity) {
355 let data = res.data as CommunityResponse;
356 this.state.loading = false;
357 this.props.onCreate(data.community);
358 } else if (res.op == UserOperation.EditCommunity) {
359 let data = res.data as CommunityResponse;
360 this.state.loading = false;
361 this.props.onEdit(data.community);