1 import { Component, linkEvent } from "inferno";
2 import { Prompt } from "inferno-router";
12 } from "lemmy-js-client";
13 import { Subscription } from "rxjs";
14 import { i18n } from "../../i18next";
15 import { UserService, WebSocketService } from "../../services";
17 capitalizeFirstLetter,
23 import { Icon, Spinner } from "../common/icon";
24 import { ImageUploadForm } from "../common/image-upload-form";
25 import { LanguageSelect } from "../common/language-select";
26 import { MarkdownTextArea } from "../common/markdown-textarea";
28 interface CommunityFormProps {
29 community_view?: CommunityView; // If a community is given, that means this is an edit
30 allLanguages: Language[];
31 siteLanguages: number[];
32 communityLanguages?: number[];
34 onCreate?(community: CommunityView): any;
35 onEdit?(community: CommunityView): any;
39 interface CommunityFormState {
47 posting_restricted_to_mods?: boolean;
48 discussion_languages?: number[];
53 export class CommunityForm extends Component<
57 private id = `community-form-${randomStr()}`;
58 private subscription?: Subscription;
60 state: CommunityFormState = {
65 constructor(props: any, context: any) {
66 super(props, context);
68 this.handleCommunityDescriptionChange =
69 this.handleCommunityDescriptionChange.bind(this);
71 this.handleIconUpload = this.handleIconUpload.bind(this);
72 this.handleIconRemove = this.handleIconRemove.bind(this);
74 this.handleBannerUpload = this.handleBannerUpload.bind(this);
75 this.handleBannerRemove = this.handleBannerRemove.bind(this);
77 this.handleDiscussionLanguageChange =
78 this.handleDiscussionLanguageChange.bind(this);
80 this.parseMessage = this.parseMessage.bind(this);
81 this.subscription = wsSubscribe(this.parseMessage);
82 const cv = this.props.community_view;
87 name: cv.community.name,
88 title: cv.community.title,
89 description: cv.community.description,
90 nsfw: cv.community.nsfw,
91 icon: cv.community.icon,
92 banner: cv.community.banner,
93 posting_restricted_to_mods: cv.community.posting_restricted_to_mods,
94 discussion_languages: this.props.communityLanguages,
101 componentDidUpdate() {
103 !this.state.loading &&
104 (this.state.form.name ||
105 this.state.form.title ||
106 this.state.form.description)
108 window.onbeforeunload = () => true;
110 window.onbeforeunload = null;
114 componentWillUnmount() {
115 this.subscription?.unsubscribe();
116 window.onbeforeunload = null;
124 !this.state.loading &&
125 (this.state.form.name ||
126 this.state.form.title ||
127 this.state.form.description)
129 message={i18n.t("block_leaving")}
131 <form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
132 {!this.props.community_view && (
133 <div className="form-group row">
135 className="col-12 col-sm-2 col-form-label"
136 htmlFor="community-name"
140 className="position-absolute pointer unselectable ml-2 text-muted"
141 data-tippy-content={i18n.t("name_explain")}
143 <Icon icon="help-circle" classes="icon-inline" />
146 <div className="col-12 col-sm-10">
150 className="form-control"
151 value={this.state.form.name}
152 onInput={linkEvent(this, this.handleCommunityNameChange)}
156 title={i18n.t("community_reqs")}
161 <div className="form-group row">
163 className="col-12 col-sm-2 col-form-label"
164 htmlFor="community-title"
166 {i18n.t("display_name")}
168 className="position-absolute pointer unselectable ml-2 text-muted"
169 data-tippy-content={i18n.t("display_name_explain")}
171 <Icon icon="help-circle" classes="icon-inline" />
174 <div className="col-12 col-sm-10">
178 value={this.state.form.title}
179 onInput={linkEvent(this, this.handleCommunityTitleChange)}
180 className="form-control"
187 <div className="form-group row">
188 <label className="col-12 col-sm-2">{i18n.t("icon")}</label>
189 <div className="col-12 col-sm-10">
191 uploadTitle={i18n.t("upload_icon")}
192 imageSrc={this.state.form.icon}
193 onUpload={this.handleIconUpload}
194 onRemove={this.handleIconRemove}
199 <div className="form-group row">
200 <label className="col-12 col-sm-2">{i18n.t("banner")}</label>
201 <div className="col-12 col-sm-10">
203 uploadTitle={i18n.t("upload_banner")}
204 imageSrc={this.state.form.banner}
205 onUpload={this.handleBannerUpload}
206 onRemove={this.handleBannerRemove}
210 <div className="form-group row">
211 <label className="col-12 col-sm-2 col-form-label" htmlFor={this.id}>
214 <div className="col-12 col-sm-10">
216 initialContent={this.state.form.description}
217 placeholder={i18n.t("description")}
218 onContentChange={this.handleCommunityDescriptionChange}
225 {this.props.enableNsfw && (
226 <div className="form-group row">
227 <legend className="col-form-label col-sm-2 pt-0">
230 <div className="col-10">
231 <div className="form-check">
233 className="form-check-input position-static"
236 checked={this.state.form.nsfw}
237 onChange={linkEvent(this, this.handleCommunityNsfwChange)}
243 <div className="form-group row">
244 <legend className="col-form-label col-6 pt-0">
245 {i18n.t("only_mods_can_post_in_community")}
247 <div className="col-6">
248 <div className="form-check">
250 className="form-check-input position-static"
251 id="community-only-mods-can-post"
253 checked={this.state.form.posting_restricted_to_mods}
256 this.handleCommunityPostingRestrictedToMods
263 allLanguages={this.props.allLanguages}
264 siteLanguages={this.props.siteLanguages}
266 selectedLanguageIds={this.state.form.discussion_languages}
268 hideLanguageWarning={true}
269 onChange={this.handleDiscussionLanguageChange}
271 <div className="form-group row">
272 <div className="col-12">
275 className="btn btn-secondary mr-2"
276 disabled={this.state.loading}
278 {this.state.loading ? (
280 ) : this.props.community_view ? (
281 capitalizeFirstLetter(i18n.t("save"))
283 capitalizeFirstLetter(i18n.t("create"))
286 {this.props.community_view && (
289 className="btn btn-secondary"
290 onClick={linkEvent(this, this.handleCancel)}
302 handleCreateCommunitySubmit(i: CommunityForm, event: any) {
303 event.preventDefault();
304 i.setState({ loading: true });
305 const cForm = i.state.form;
306 const auth = myAuth();
308 const cv = i.props.community_view;
312 const form: EditCommunity = {
313 community_id: cv.community.id,
315 description: cForm.description,
317 banner: cForm.banner,
319 posting_restricted_to_mods: cForm.posting_restricted_to_mods,
320 discussion_languages: cForm.discussion_languages,
324 WebSocketService.Instance.send(wsClient.editCommunity(form));
326 if (cForm.title && cForm.name) {
327 const form: CreateCommunity = {
330 description: cForm.description,
332 banner: cForm.banner,
334 posting_restricted_to_mods: cForm.posting_restricted_to_mods,
335 discussion_languages: cForm.discussion_languages,
338 WebSocketService.Instance.send(wsClient.createCommunity(form));
345 handleCommunityNameChange(i: CommunityForm, event: any) {
346 i.state.form.name = event.target.value;
350 handleCommunityTitleChange(i: CommunityForm, event: any) {
351 i.state.form.title = event.target.value;
355 handleCommunityDescriptionChange(val: string) {
356 this.setState(s => ((s.form.description = val), s));
359 handleCommunityNsfwChange(i: CommunityForm, event: any) {
360 i.state.form.nsfw = event.target.checked;
364 handleCommunityPostingRestrictedToMods(i: CommunityForm, event: any) {
365 i.state.form.posting_restricted_to_mods = event.target.checked;
369 handleCancel(i: CommunityForm) {
370 i.props.onCancel?.();
373 handleIconUpload(url: string) {
374 this.setState(s => ((s.form.icon = url), s));
378 this.setState(s => ((s.form.icon = ""), s));
381 handleBannerUpload(url: string) {
382 this.setState(s => ((s.form.banner = url), s));
385 handleBannerRemove() {
386 this.setState(s => ((s.form.banner = ""), s));
389 handleDiscussionLanguageChange(val: number[]) {
390 this.setState(s => ((s.form.discussion_languages = val), s));
393 parseMessage(msg: any) {
394 const op = wsUserOp(msg);
397 // Errors handled by top level pages
398 // toast(i18n.t(msg.error), "danger");
399 this.setState({ loading: false });
401 } else if (op == UserOperation.CreateCommunity) {
402 const data = wsJsonToRes<CommunityResponse>(msg);
403 this.props.onCreate?.(data.community_view);
406 const community = data.community_view.community;
408 const mui = UserService.Instance.myUserInfo;
410 const person = mui.local_user_view.person;
420 } else if (op == UserOperation.EditCommunity) {
421 const data = wsJsonToRes<CommunityResponse>(msg);
422 this.setState({ loading: false });
423 this.props.onEdit?.(data.community_view);
424 const community = data.community_view.community;
426 const mui = UserService.Instance.myUserInfo;
428 const followFound = mui.follows.findIndex(
429 f => f.community.id == community.id
432 mui.follows[followFound].community = community;
435 const moderatesFound = mui.moderates.findIndex(
436 f => f.community.id == community.id
438 if (moderatesFound) {
439 mui.moderates[moderatesFound].community = community;