1 import { None, Option, Some } from "@sniptt/monads";
2 import { Component, linkEvent } from "inferno";
3 import { Prompt } from "inferno-router";
13 } from "lemmy-js-client";
14 import { Subscription } from "rxjs";
15 import { i18n } from "../../i18next";
16 import { UserService, WebSocketService } from "../../services";
19 capitalizeFirstLetter,
24 import { Icon, Spinner } from "../common/icon";
25 import { ImageUploadForm } from "../common/image-upload-form";
26 import { MarkdownTextArea } from "../common/markdown-textarea";
28 interface CommunityFormProps {
29 community_view: Option<CommunityView>; // If a community is given, that means this is an edit
31 onCreate?(community: CommunityView): any;
32 onEdit?(community: CommunityView): any;
36 interface CommunityFormState {
37 communityForm: CreateCommunity;
41 export class CommunityForm extends Component<
45 private id = `community-form-${randomStr()}`;
46 private subscription: Subscription;
48 private emptyState: CommunityFormState = {
49 communityForm: new CreateCommunity({
56 posting_restricted_to_mods: None,
62 constructor(props: any, context: any) {
63 super(props, context);
65 this.state = this.emptyState;
67 this.handleCommunityDescriptionChange =
68 this.handleCommunityDescriptionChange.bind(this);
70 this.handleIconUpload = this.handleIconUpload.bind(this);
71 this.handleIconRemove = this.handleIconRemove.bind(this);
73 this.handleBannerUpload = this.handleBannerUpload.bind(this);
74 this.handleBannerRemove = this.handleBannerRemove.bind(this);
76 this.parseMessage = this.parseMessage.bind(this);
77 this.subscription = wsSubscribe(this.parseMessage);
79 if (this.props.community_view.isSome()) {
80 let cv = this.props.community_view.unwrap();
83 communityForm: new CreateCommunity({
84 name: cv.community.name,
85 title: cv.community.title,
86 description: cv.community.description,
87 nsfw: Some(cv.community.nsfw),
88 icon: cv.community.icon,
89 banner: cv.community.banner,
90 posting_restricted_to_mods: Some(
91 cv.community.posting_restricted_to_mods
99 componentDidUpdate() {
101 !this.state.loading &&
102 (this.state.communityForm.name ||
103 this.state.communityForm.title ||
104 this.state.communityForm.description.isSome())
106 window.onbeforeunload = () => true;
108 window.onbeforeunload = undefined;
112 componentWillUnmount() {
113 this.subscription.unsubscribe();
114 window.onbeforeunload = null;
122 !this.state.loading &&
123 (this.state.communityForm.name ||
124 this.state.communityForm.title ||
125 this.state.communityForm.description.isSome())
127 message={i18n.t("block_leaving")}
129 <form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
130 {this.props.community_view.isNone() && (
131 <div className="form-group row">
133 className="col-12 col-sm-2 col-form-label"
134 htmlFor="community-name"
138 className="position-absolute pointer unselectable ml-2 text-muted"
139 data-tippy-content={i18n.t("name_explain")}
141 <Icon icon="help-circle" classes="icon-inline" />
144 <div className="col-12 col-sm-10">
148 className="form-control"
149 value={this.state.communityForm.name}
150 onInput={linkEvent(this, this.handleCommunityNameChange)}
154 title={i18n.t("community_reqs")}
159 <div className="form-group row">
161 className="col-12 col-sm-2 col-form-label"
162 htmlFor="community-title"
164 {i18n.t("display_name")}
166 className="position-absolute pointer unselectable ml-2 text-muted"
167 data-tippy-content={i18n.t("display_name_explain")}
169 <Icon icon="help-circle" classes="icon-inline" />
172 <div className="col-12 col-sm-10">
176 value={this.state.communityForm.title}
177 onInput={linkEvent(this, this.handleCommunityTitleChange)}
178 className="form-control"
185 <div className="form-group row">
186 <label className="col-12 col-sm-2">{i18n.t("icon")}</label>
187 <div className="col-12 col-sm-10">
189 uploadTitle={i18n.t("upload_icon")}
190 imageSrc={this.state.communityForm.icon}
191 onUpload={this.handleIconUpload}
192 onRemove={this.handleIconRemove}
197 <div className="form-group row">
198 <label className="col-12 col-sm-2">{i18n.t("banner")}</label>
199 <div className="col-12 col-sm-10">
201 uploadTitle={i18n.t("upload_banner")}
202 imageSrc={this.state.communityForm.banner}
203 onUpload={this.handleBannerUpload}
204 onRemove={this.handleBannerRemove}
208 <div className="form-group row">
209 <label className="col-12 col-sm-2 col-form-label" htmlFor={this.id}>
212 <div className="col-12 col-sm-10">
214 initialContent={this.state.communityForm.description}
215 initialLanguageId={None}
216 placeholder={Some("description")}
219 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={toUndefined(this.state.communityForm.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={toUndefined(
254 this.state.communityForm.posting_restricted_to_mods
258 this.handleCommunityPostingRestrictedToMods
264 <div className="form-group row">
265 <div className="col-12">
268 className="btn btn-secondary mr-2"
269 disabled={this.state.loading}
271 {this.state.loading ? (
273 ) : this.props.community_view.isSome() ? (
274 capitalizeFirstLetter(i18n.t("save"))
276 capitalizeFirstLetter(i18n.t("create"))
279 {this.props.community_view.isSome() && (
282 className="btn btn-secondary"
283 onClick={linkEvent(this, this.handleCancel)}
295 handleCreateCommunitySubmit(i: CommunityForm, event: any) {
296 event.preventDefault();
297 i.setState({ loading: true });
298 let cForm = i.state.communityForm;
299 cForm.auth = auth().unwrap();
301 i.props.community_view.match({
303 let form = new EditCommunity({
304 community_id: cv.community.id,
305 title: Some(cForm.title),
306 description: cForm.description,
308 banner: cForm.banner,
310 posting_restricted_to_mods: cForm.posting_restricted_to_mods,
314 WebSocketService.Instance.send(wsClient.editCommunity(form));
317 WebSocketService.Instance.send(
318 wsClient.createCommunity(i.state.communityForm)
325 handleCommunityNameChange(i: CommunityForm, event: any) {
326 i.state.communityForm.name = event.target.value;
330 handleCommunityTitleChange(i: CommunityForm, event: any) {
331 i.state.communityForm.title = event.target.value;
335 handleCommunityDescriptionChange(val: string) {
336 this.setState(s => ((s.communityForm.description = Some(val)), s));
339 handleCommunityNsfwChange(i: CommunityForm, event: any) {
340 i.state.communityForm.nsfw = Some(event.target.checked);
344 handleCommunityPostingRestrictedToMods(i: CommunityForm, event: any) {
345 i.state.communityForm.posting_restricted_to_mods = Some(
351 handleCancel(i: CommunityForm) {
355 handleIconUpload(url: string) {
356 this.setState(s => ((s.communityForm.icon = Some(url)), s));
360 this.setState(s => ((s.communityForm.icon = Some("")), s));
363 handleBannerUpload(url: string) {
364 this.setState(s => ((s.communityForm.banner = Some(url)), s));
367 handleBannerRemove() {
368 this.setState(s => ((s.communityForm.banner = Some("")), s));
371 parseMessage(msg: any) {
372 let op = wsUserOp(msg);
375 // Errors handled by top level pages
376 // toast(i18n.t(msg.error), "danger");
377 this.setState({ loading: false });
379 } else if (op == UserOperation.CreateCommunity) {
380 let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
381 this.props.onCreate(data.community_view);
384 let community = data.community_view.community;
386 UserService.Instance.myUserInfo.match({
388 let person = mui.local_user_view.person;
400 } else if (op == UserOperation.EditCommunity) {
401 let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
402 this.setState({ loading: false });
403 this.props.onEdit(data.community_view);
404 let community = data.community_view.community;
406 UserService.Instance.myUserInfo.match({
408 let followFound = mui.follows.findIndex(
409 f => f.community.id == community.id
412 mui.follows[followFound].community = community;
415 let moderatesFound = mui.moderates.findIndex(
416 f => f.community.id == community.id
418 if (moderatesFound) {
419 mui.moderates[moderatesFound].community = community;