+import { myAuthRequired } from "@utils/app";
+import { capitalizeFirstLetter, randomStr } from "@utils/helpers";
import { Component, linkEvent } from "inferno";
import { Prompt } from "inferno-router";
import {
- CommunityResponse,
CommunityView,
CreateCommunity,
EditCommunity,
Language,
- UserOperation,
- wsJsonToRes,
- wsUserOp,
} from "lemmy-js-client";
-import { Subscription } from "rxjs";
-import { i18n } from "../../i18next";
-import { UserService, WebSocketService } from "../../services";
-import {
- capitalizeFirstLetter,
- myAuth,
- randomStr,
- wsClient,
- wsSubscribe,
-} from "../../utils";
+import { I18NextService } from "../../services";
import { Icon, Spinner } from "../common/icon";
import { ImageUploadForm } from "../common/image-upload-form";
import { LanguageSelect } from "../common/language-select";
siteLanguages: number[];
communityLanguages?: number[];
onCancel?(): any;
- onCreate?(community: CommunityView): any;
- onEdit?(community: CommunityView): any;
+ onUpsertCommunity(form: CreateCommunity | EditCommunity): void;
enableNsfw?: boolean;
+ loading?: boolean;
}
interface CommunityFormState {
posting_restricted_to_mods?: boolean;
discussion_languages?: number[];
};
- loading: boolean;
+ submitted: boolean;
}
export class CommunityForm extends Component<
CommunityFormState
> {
private id = `community-form-${randomStr()}`;
- private subscription?: Subscription;
state: CommunityFormState = {
form: {},
- loading: false,
+ submitted: false,
};
constructor(props: any, context: any) {
this.handleDiscussionLanguageChange =
this.handleDiscussionLanguageChange.bind(this);
- this.parseMessage = this.parseMessage.bind(this);
- this.subscription = wsSubscribe(this.parseMessage);
- let cv = this.props.community_view;
+ const cv = this.props.community_view;
if (cv) {
this.state = {
+ ...this.state,
form: {
name: cv.community.name,
title: cv.community.title,
posting_restricted_to_mods: cv.community.posting_restricted_to_mods,
discussion_languages: this.props.communityLanguages,
},
- loading: false,
};
}
}
- componentDidUpdate() {
- if (
- !this.state.loading &&
- (this.state.form.name ||
- this.state.form.title ||
- this.state.form.description)
- ) {
- window.onbeforeunload = () => true;
- } else {
- window.onbeforeunload = null;
- }
- }
-
- componentWillUnmount() {
- this.subscription?.unsubscribe();
- window.onbeforeunload = null;
- }
-
render() {
return (
- <>
+ <form
+ className="community-form"
+ onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}
+ >
<Prompt
+ message={I18NextService.i18n.t("block_leaving")}
when={
- !this.state.loading &&
- (this.state.form.name ||
+ !this.props.loading &&
+ !!(
+ this.state.form.name ||
this.state.form.title ||
- this.state.form.description)
+ this.state.form.description
+ ) &&
+ !this.state.submitted
}
- message={i18n.t("block_leaving")}
/>
- <form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
- {!this.props.community_view && (
- <div className="form-group row">
- <label
- className="col-12 col-sm-2 col-form-label"
- htmlFor="community-name"
- >
- {i18n.t("name")}
- <span
- className="position-absolute pointer unselectable ml-2 text-muted"
- data-tippy-content={i18n.t("name_explain")}
- >
- <Icon icon="help-circle" classes="icon-inline" />
- </span>
- </label>
- <div className="col-12 col-sm-10">
- <input
- type="text"
- id="community-name"
- className="form-control"
- value={this.state.form.name}
- onInput={linkEvent(this, this.handleCommunityNameChange)}
- required
- minLength={3}
- pattern="[a-z0-9_]+"
- title={i18n.t("community_reqs")}
- />
- </div>
- </div>
- )}
- <div className="form-group row">
+ {!this.props.community_view && (
+ <div className="mb-3 row">
<label
className="col-12 col-sm-2 col-form-label"
- htmlFor="community-title"
+ htmlFor="community-name"
>
- {i18n.t("display_name")}
+ {I18NextService.i18n.t("name")}
<span
- className="position-absolute pointer unselectable ml-2 text-muted"
- data-tippy-content={i18n.t("display_name_explain")}
+ className="position-absolute pointer unselectable ms-2 text-muted"
+ data-tippy-content={I18NextService.i18n.t("name_explain")}
>
<Icon icon="help-circle" classes="icon-inline" />
</span>
<div className="col-12 col-sm-10">
<input
type="text"
- id="community-title"
- value={this.state.form.title}
- onInput={linkEvent(this, this.handleCommunityTitleChange)}
+ id="community-name"
className="form-control"
+ value={this.state.form.name}
+ onInput={linkEvent(this, this.handleCommunityNameChange)}
required
minLength={3}
- maxLength={100}
+ pattern="[a-z0-9_]+"
+ title={I18NextService.i18n.t("community_reqs")}
/>
</div>
</div>
- <div className="form-group row">
- <label className="col-12 col-sm-2">{i18n.t("icon")}</label>
- <div className="col-12 col-sm-10">
- <ImageUploadForm
- uploadTitle={i18n.t("upload_icon")}
- imageSrc={this.state.form.icon}
- onUpload={this.handleIconUpload}
- onRemove={this.handleIconRemove}
- rounded
- />
- </div>
+ )}
+ <div className="mb-3 row">
+ <label
+ className="col-12 col-sm-2 col-form-label"
+ htmlFor="community-title"
+ >
+ {I18NextService.i18n.t("display_name")}
+ <span
+ className="position-absolute pointer unselectable ms-2 text-muted"
+ data-tippy-content={I18NextService.i18n.t("display_name_explain")}
+ >
+ <Icon icon="help-circle" classes="icon-inline" />
+ </span>
+ </label>
+ <div className="col-12 col-sm-10">
+ <input
+ type="text"
+ id="community-title"
+ value={this.state.form.title}
+ onInput={linkEvent(this, this.handleCommunityTitleChange)}
+ className="form-control"
+ required
+ minLength={3}
+ maxLength={100}
+ />
</div>
- <div className="form-group row">
- <label className="col-12 col-sm-2">{i18n.t("banner")}</label>
- <div className="col-12 col-sm-10">
- <ImageUploadForm
- uploadTitle={i18n.t("upload_banner")}
- imageSrc={this.state.form.banner}
- onUpload={this.handleBannerUpload}
- onRemove={this.handleBannerRemove}
- />
- </div>
+ </div>
+ <div className="mb-3 row">
+ <label className="col-12 col-sm-2 col-form-label">
+ {I18NextService.i18n.t("icon")}
+ </label>
+ <div className="col-12 col-sm-10">
+ <ImageUploadForm
+ uploadTitle={I18NextService.i18n.t("upload_icon")}
+ imageSrc={this.state.form.icon}
+ onUpload={this.handleIconUpload}
+ onRemove={this.handleIconRemove}
+ rounded
+ />
</div>
- <div className="form-group row">
- <label className="col-12 col-sm-2 col-form-label" htmlFor={this.id}>
- {i18n.t("sidebar")}
- </label>
- <div className="col-12 col-sm-10">
- <MarkdownTextArea
- initialContent={this.state.form.description}
- placeholder={i18n.t("description")}
- onContentChange={this.handleCommunityDescriptionChange}
- allLanguages={[]}
- siteLanguages={[]}
- />
- </div>
+ </div>
+ <div className="mb-3 row">
+ <label className="col-12 col-sm-2 col-form-label">
+ {I18NextService.i18n.t("banner")}
+ </label>
+ <div className="col-12 col-sm-10">
+ <ImageUploadForm
+ uploadTitle={I18NextService.i18n.t("upload_banner")}
+ imageSrc={this.state.form.banner}
+ onUpload={this.handleBannerUpload}
+ onRemove={this.handleBannerRemove}
+ />
+ </div>
+ </div>
+ <div className="mb-3 row">
+ <label className="col-12 col-sm-2 col-form-label" htmlFor={this.id}>
+ {I18NextService.i18n.t("sidebar")}
+ </label>
+ <div className="col-12 col-sm-10">
+ <MarkdownTextArea
+ initialContent={this.state.form.description}
+ placeholder={I18NextService.i18n.t("description") ?? undefined}
+ onContentChange={this.handleCommunityDescriptionChange}
+ hideNavigationWarnings
+ allLanguages={[]}
+ siteLanguages={[]}
+ />
</div>
+ </div>
- {this.props.enableNsfw && (
- <div className="form-group row">
- <legend className="col-form-label col-sm-2 pt-0">
- {i18n.t("nsfw")}
- </legend>
- <div className="col-10">
- <div className="form-check">
- <input
- className="form-check-input position-static"
- id="community-nsfw"
- type="checkbox"
- checked={this.state.form.nsfw}
- onChange={linkEvent(this, this.handleCommunityNsfwChange)}
- />
- </div>
- </div>
- </div>
- )}
- <div className="form-group row">
- <legend className="col-form-label col-6 pt-0">
- {i18n.t("only_mods_can_post_in_community")}
+ {this.props.enableNsfw && (
+ <div className="mb-3 row">
+ <legend className="col-form-label col-sm-2 pt-0">
+ {I18NextService.i18n.t("nsfw")}
</legend>
- <div className="col-6">
+ <div className="col-10">
<div className="form-check">
<input
className="form-check-input position-static"
- id="community-only-mods-can-post"
+ id="community-nsfw"
type="checkbox"
- checked={this.state.form.posting_restricted_to_mods}
- onChange={linkEvent(
- this,
- this.handleCommunityPostingRestrictedToMods
- )}
+ checked={this.state.form.nsfw}
+ onChange={linkEvent(this, this.handleCommunityNsfwChange)}
/>
</div>
</div>
</div>
- <LanguageSelect
- allLanguages={this.props.allLanguages}
- siteLanguages={this.props.siteLanguages}
- showSite
- selectedLanguageIds={this.state.form.discussion_languages}
- multiple={true}
- onChange={this.handleDiscussionLanguageChange}
- />
- <div className="form-group row">
- <div className="col-12">
+ )}
+ <div className="mb-3 row">
+ <legend className="col-form-label col-6 pt-0">
+ {I18NextService.i18n.t("only_mods_can_post_in_community")}
+ </legend>
+ <div className="col-6">
+ <div className="form-check">
+ <input
+ className="form-check-input position-static"
+ id="community-only-mods-can-post"
+ type="checkbox"
+ checked={this.state.form.posting_restricted_to_mods}
+ onChange={linkEvent(
+ this,
+ this.handleCommunityPostingRestrictedToMods,
+ )}
+ />
+ </div>
+ </div>
+ </div>
+ <LanguageSelect
+ allLanguages={this.props.allLanguages}
+ siteLanguages={this.props.siteLanguages}
+ showSite
+ selectedLanguageIds={this.state.form.discussion_languages}
+ multiple={true}
+ onChange={this.handleDiscussionLanguageChange}
+ />
+ <div className="mb-3 row">
+ <div className="col-12">
+ <button
+ type="submit"
+ className="btn btn-secondary me-2"
+ disabled={this.props.loading}
+ >
+ {this.props.loading ? (
+ <Spinner />
+ ) : this.props.community_view ? (
+ capitalizeFirstLetter(I18NextService.i18n.t("save"))
+ ) : (
+ capitalizeFirstLetter(I18NextService.i18n.t("create"))
+ )}
+ </button>
+ {this.props.community_view && (
<button
- type="submit"
- className="btn btn-secondary mr-2"
- disabled={this.state.loading}
+ type="button"
+ className="btn btn-secondary"
+ onClick={linkEvent(this, this.handleCancel)}
>
- {this.state.loading ? (
- <Spinner />
- ) : this.props.community_view ? (
- capitalizeFirstLetter(i18n.t("save"))
- ) : (
- capitalizeFirstLetter(i18n.t("create"))
- )}
+ {I18NextService.i18n.t("cancel")}
</button>
- {this.props.community_view && (
- <button
- type="button"
- className="btn btn-secondary"
- onClick={linkEvent(this, this.handleCancel)}
- >
- {i18n.t("cancel")}
- </button>
- )}
- </div>
+ )}
</div>
- </form>
- </>
+ </div>
+ </form>
);
}
handleCreateCommunitySubmit(i: CommunityForm, event: any) {
event.preventDefault();
- i.setState({ loading: true });
- let cForm = i.state.form;
- let auth = myAuth();
+ i.setState({ submitted: true });
+ const cForm = i.state.form;
+ const auth = myAuthRequired();
- let cv = i.props.community_view;
+ const cv = i.props.community_view;
- if (auth) {
- if (cv) {
- let form: EditCommunity = {
- community_id: cv.community.id,
+ if (cv) {
+ i.props.onUpsertCommunity({
+ community_id: cv.community.id,
+ title: cForm.title,
+ description: cForm.description,
+ icon: cForm.icon,
+ banner: cForm.banner,
+ nsfw: cForm.nsfw,
+ posting_restricted_to_mods: cForm.posting_restricted_to_mods,
+ discussion_languages: cForm.discussion_languages,
+ auth,
+ });
+ } else {
+ if (cForm.title && cForm.name) {
+ i.props.onUpsertCommunity({
+ name: cForm.name,
title: cForm.title,
description: cForm.description,
icon: cForm.icon,
posting_restricted_to_mods: cForm.posting_restricted_to_mods,
discussion_languages: cForm.discussion_languages,
auth,
- };
-
- WebSocketService.Instance.send(wsClient.editCommunity(form));
- } else {
- if (cForm.title && cForm.name) {
- let form: CreateCommunity = {
- name: cForm.name,
- title: cForm.title,
- description: cForm.description,
- icon: cForm.icon,
- banner: cForm.banner,
- nsfw: cForm.nsfw,
- posting_restricted_to_mods: cForm.posting_restricted_to_mods,
- discussion_languages: cForm.discussion_languages,
- auth,
- };
- WebSocketService.Instance.send(wsClient.createCommunity(form));
- }
+ });
}
}
- i.setState(i.state);
}
handleCommunityNameChange(i: CommunityForm, event: any) {
- i.state.form.name = event.target.value;
- i.setState(i.state);
+ i.setState(s => ((s.form.name = event.target.value), s));
}
handleCommunityTitleChange(i: CommunityForm, event: any) {
- i.state.form.title = event.target.value;
- i.setState(i.state);
+ i.setState(s => ((s.form.title = event.target.value), s));
}
handleCommunityDescriptionChange(val: string) {
}
handleCommunityNsfwChange(i: CommunityForm, event: any) {
- i.state.form.nsfw = event.target.checked;
- i.setState(i.state);
+ i.setState(s => ((s.form.nsfw = event.target.checked), s));
}
handleCommunityPostingRestrictedToMods(i: CommunityForm, event: any) {
- i.state.form.posting_restricted_to_mods = event.target.checked;
- i.setState(i.state);
+ i.setState(
+ s => ((s.form.posting_restricted_to_mods = event.target.checked), s),
+ );
}
handleCancel(i: CommunityForm) {
handleDiscussionLanguageChange(val: number[]) {
this.setState(s => ((s.form.discussion_languages = val), s));
}
-
- parseMessage(msg: any) {
- let op = wsUserOp(msg);
- console.log(msg);
- if (msg.error) {
- // Errors handled by top level pages
- // toast(i18n.t(msg.error), "danger");
- this.setState({ loading: false });
- return;
- } else if (op == UserOperation.CreateCommunity) {
- let data = wsJsonToRes<CommunityResponse>(msg);
- this.props.onCreate?.(data.community_view);
-
- // Update myUserInfo
- let community = data.community_view.community;
-
- let mui = UserService.Instance.myUserInfo;
- if (mui) {
- let person = mui.local_user_view.person;
- mui.follows.push({
- community,
- follower: person,
- });
- mui.moderates.push({
- community,
- moderator: person,
- });
- }
- } else if (op == UserOperation.EditCommunity) {
- let data = wsJsonToRes<CommunityResponse>(msg);
- this.setState({ loading: false });
- this.props.onEdit?.(data.community_view);
- let community = data.community_view.community;
-
- let mui = UserService.Instance.myUserInfo;
- if (mui) {
- let followFound = mui.follows.findIndex(
- f => f.community.id == community.id
- );
- if (followFound) {
- mui.follows[followFound].community = community;
- }
-
- let moderatesFound = mui.moderates.findIndex(
- f => f.community.id == community.id
- );
- if (moderatesFound) {
- mui.moderates[moderatesFound].community = community;
- }
- }
- }
- }
}