+import { myAuthRequired } from "@utils/app";
+import { capitalizeFirstLetter, validInstanceTLD } from "@utils/helpers";
import {
Component,
InfernoKeyboardEvent,
InfernoMouseEvent,
+ InfernoNode,
linkEvent,
} from "inferno";
import {
Instance,
ListingType,
} from "lemmy-js-client";
-import { i18n } from "../../i18next";
-import { capitalizeFirstLetter, myAuthRequired } from "../../utils";
+import deepEqual from "lodash.isequal";
+import { I18NextService } from "../../services";
import { Icon, Spinner } from "../common/icon";
import { ImageUploadForm } from "../common/image-upload-form";
import { LanguageSelect } from "../common/language-select";
themeList?: string[];
onSaveSite(form: EditSite): void;
siteRes: GetSiteResponse;
+ loading: boolean;
}
interface SiteFormState {
siteForm: EditSite;
- loading: boolean;
instance_select: {
allowed_instances: string;
blocked_instances: string;
export class SiteForm extends Component<SiteFormProps, SiteFormState> {
state: SiteFormState = {
siteForm: this.initSiteForm(),
- loading: false,
instance_select: {
allowed_instances: "",
blocked_instances: "",
initSiteForm(): EditSite {
const site = this.props.siteRes.site_view.site;
const ls = this.props.siteRes.site_view.local_site;
+
return {
name: site.name,
sidebar: site.sidebar,
slur_filter_regex: ls.slur_filter_regex,
actor_name_max_length: ls.actor_name_max_length,
federation_enabled: ls.federation_enabled,
- federation_debug: ls.federation_debug,
- federation_worker_count: ls.federation_worker_count,
captcha_enabled: ls.captcha_enabled,
captcha_difficulty: ls.captcha_difficulty,
allowed_instances: this.props.allowedInstances?.map(i => i.domain),
this.handleDiscussionLanguageChange =
this.handleDiscussionLanguageChange.bind(this);
+
this.handleAddInstance = this.handleAddInstance.bind(this);
+ this.handleRemoveInstance = this.handleRemoveInstance.bind(this);
+
this.handleInstanceEnterPress = this.handleInstanceEnterPress.bind(this);
this.handleInstanceTextChange = this.handleInstanceTextChange.bind(this);
}
- // Necessary to stop the loading
- componentWillReceiveProps() {
- this.setState({ loading: false });
- }
-
render() {
const siteSetup = this.props.siteRes.site_view.local_site.site_setup;
return (
- <form onSubmit={linkEvent(this, this.handleSaveSiteSubmit)}>
+ <form
+ className="site-form"
+ onSubmit={linkEvent(this, this.handleSaveSiteSubmit)}
+ >
<NavigationPrompt
when={
- !this.state.loading &&
+ !this.props.loading &&
!siteSetup &&
!!(
this.state.siteForm.name ||
/>
<h5>{`${
siteSetup
- ? capitalizeFirstLetter(i18n.t("save"))
- : capitalizeFirstLetter(i18n.t("name"))
- } ${i18n.t("your_site")}`}</h5>
- <div className="form-group row">
+ ? capitalizeFirstLetter(I18NextService.i18n.t("edit"))
+ : capitalizeFirstLetter(I18NextService.i18n.t("setup"))
+ } ${I18NextService.i18n.t("your_site")}`}</h5>
+ <div className="mb-3 row">
<label className="col-12 col-form-label" htmlFor="create-site-name">
- {i18n.t("name")}
+ {I18NextService.i18n.t("name")}
</label>
<div className="col-12">
<input
/>
</div>
</div>
- <div className="form-group">
- <label>{i18n.t("icon")}</label>
- <ImageUploadForm
- uploadTitle={i18n.t("upload_icon")}
- imageSrc={this.state.siteForm.icon}
- onUpload={this.handleIconUpload}
- onRemove={this.handleIconRemove}
- rounded
- />
+ <div className="row mb-3">
+ <label className="col-sm-2 col-form-label">
+ {I18NextService.i18n.t("icon")}
+ </label>
+ <div className="col-sm-10">
+ <ImageUploadForm
+ uploadTitle={I18NextService.i18n.t("upload_icon")}
+ imageSrc={this.state.siteForm.icon}
+ onUpload={this.handleIconUpload}
+ onRemove={this.handleIconRemove}
+ rounded
+ />
+ </div>
</div>
- <div className="form-group">
- <label>{i18n.t("banner")}</label>
- <ImageUploadForm
- uploadTitle={i18n.t("upload_banner")}
- imageSrc={this.state.siteForm.banner}
- onUpload={this.handleBannerUpload}
- onRemove={this.handleBannerRemove}
- />
+ <div className="row mb-3">
+ <label className="col-sm-2 col-form-label">
+ {I18NextService.i18n.t("banner")}
+ </label>
+ <div className="col-sm-10">
+ <ImageUploadForm
+ uploadTitle={I18NextService.i18n.t("upload_banner")}
+ imageSrc={this.state.siteForm.banner}
+ onUpload={this.handleBannerUpload}
+ onRemove={this.handleBannerRemove}
+ />
+ </div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<label className="col-12 col-form-label" htmlFor="site-desc">
- {i18n.t("description")}
+ {I18NextService.i18n.t("description")}
</label>
<div className="col-12">
<input
/>
</div>
</div>
- <div className="form-group row">
- <label className="col-12 col-form-label">{i18n.t("sidebar")}</label>
+ <div className="mb-3 row">
+ <label className="col-12 col-form-label">
+ {I18NextService.i18n.t("sidebar")}
+ </label>
<div className="col-12">
<MarkdownTextArea
initialContent={this.state.siteForm.sidebar}
/>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<label className="col-12 col-form-label">
- {i18n.t("legal_information")}
+ {I18NextService.i18n.t("legal_information")}
</label>
<div className="col-12">
<MarkdownTextArea
/>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<div className="form-check">
<input
className="form-check-label"
htmlFor="create-site-downvotes"
>
- {i18n.t("enable_downvotes")}
+ {I18NextService.i18n.t("enable_downvotes")}
</label>
</div>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<div className="form-check">
<input
className="form-check-label"
htmlFor="create-site-enable-nsfw"
>
- {i18n.t("enable_nsfw")}
+ {I18NextService.i18n.t("enable_nsfw")}
</label>
</div>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<label
- className="form-check-label mr-2"
+ className="form-check-label me-2"
htmlFor="create-site-registration-mode"
>
- {i18n.t("registration_mode")}
+ {I18NextService.i18n.t("registration_mode")}
</label>
<select
id="create-site-registration-mode"
value={this.state.siteForm.registration_mode}
onChange={linkEvent(this, this.handleSiteRegistrationModeChange)}
- className="custom-select w-auto"
+ className="form-select d-inline-block w-auto"
>
<option value={"RequireApplication"}>
- {i18n.t("require_registration_application")}
+ {I18NextService.i18n.t("require_registration_application")}
+ </option>
+ <option value={"Open"}>
+ {I18NextService.i18n.t("open_registration")}
+ </option>
+ <option value={"Closed"}>
+ {I18NextService.i18n.t("close_registration")}
</option>
- <option value={"Open"}>{i18n.t("open_registration")}</option>
- <option value={"Closed"}>{i18n.t("close_registration")}</option>
</select>
</div>
</div>
{this.state.siteForm.registration_mode == "RequireApplication" && (
- <div className="form-group row">
+ <div className="mb-3 row">
<label className="col-12 col-form-label">
- {i18n.t("application_questionnaire")}
+ {I18NextService.i18n.t("application_questionnaire")}
</label>
<div className="col-12">
<MarkdownTextArea
</div>
</div>
)}
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<div className="form-check">
<input
className="form-check-label"
htmlFor="create-site-community-creation-admin-only"
>
- {i18n.t("community_creation_admin_only")}
+ {I18NextService.i18n.t("community_creation_admin_only")}
</label>
</div>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<div className="form-check">
<input
className="form-check-label"
htmlFor="create-site-require-email-verification"
>
- {i18n.t("require_email_verification")}
+ {I18NextService.i18n.t("require_email_verification")}
</label>
</div>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<div className="form-check">
<input
className="form-check-label"
htmlFor="create-site-email-admins"
>
- {i18n.t("application_email_admins")}
+ {I18NextService.i18n.t("application_email_admins")}
</label>
</div>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<div className="form-check">
<input
className="form-check-label"
htmlFor="create-site-reports-email-admins"
>
- {i18n.t("reports_email_admins")}
+ {I18NextService.i18n.t("reports_email_admins")}
</label>
</div>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<label
- className="form-check-label mr-2"
+ className="form-check-label me-2"
htmlFor="create-site-default-theme"
>
- {i18n.t("theme")}
+ {I18NextService.i18n.t("theme")}
</label>
<select
id="create-site-default-theme"
value={this.state.siteForm.default_theme}
onChange={linkEvent(this, this.handleSiteDefaultTheme)}
- className="custom-select w-auto"
+ className="form-select d-inline-block w-auto"
>
- <option value="browser">{i18n.t("browser_default")}</option>
+ <option value="browser">
+ {I18NextService.i18n.t("browser_default")}
+ </option>
{this.props.themeList?.map(theme => (
<option key={theme} value={theme}>
{theme}
</div>
</div>
{this.props.showLocal && (
- <form className="form-group row">
- <label className="col-sm-3">{i18n.t("listing_type")}</label>
+ <form className="mb-3 row">
+ <label className="col-sm-3 col-form-label">
+ {I18NextService.i18n.t("listing_type")}
+ </label>
<div className="col-sm-9">
<ListingTypeSelect
type_={this.state.siteForm.default_post_listing_type ?? "Local"}
</div>
</form>
)}
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<div className="form-check">
<input
className="form-check-label"
htmlFor="create-site-private-instance"
>
- {i18n.t("private_instance")}
+ {I18NextService.i18n.t("private_instance")}
</label>
</div>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<div className="form-check">
<input
className="form-check-label"
htmlFor="create-site-hide-modlog-mod-names"
>
- {i18n.t("hide_modlog_mod_names")}
+ {I18NextService.i18n.t("hide_modlog_mod_names")}
</label>
</div>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<label
className="col-12 col-form-label"
htmlFor="create-site-slur-filter-regex"
>
- {i18n.t("slur_filter_regex")}
+ {I18NextService.i18n.t("slur_filter_regex")}
</label>
<div className="col-12">
<input
onChange={this.handleDiscussionLanguageChange}
showAll
/>
- <div className="form-group row">
+ <div className="mb-3 row">
<label
className="col-12 col-form-label"
htmlFor="create-site-actor-name"
>
- {i18n.t("actor_name_max_length")}
+ {I18NextService.i18n.t("actor_name_max_length")}
</label>
<div className="col-12">
<input
/>
</div>
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<div className="form-check">
<input
className="form-check-label"
htmlFor="create-site-federation-enabled"
>
- {i18n.t("federation_enabled")}
+ {I18NextService.i18n.t("federation_enabled")}
</label>
</div>
</div>
</div>
{this.state.siteForm.federation_enabled && (
<>
- <div className="form-group row">
+ <div className="mb-3 row">
{this.federatedInstanceSelect("allowed_instances")}
{this.federatedInstanceSelect("blocked_instances")}
</div>
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<div className="form-check">
<input
className="form-check-label"
htmlFor="create-site-federation-debug"
>
- {i18n.t("federation_debug")}
+ {I18NextService.i18n.t("federation_debug")}
</label>
</div>
</div>
</div>
- <div className="form-group row">
- <label
- className="col-12 col-form-label"
- htmlFor="create-site-federation-worker-count"
- >
- {i18n.t("federation_worker_count")}
- </label>
- <div className="col-12">
- <input
- type="number"
- id="create-site-federation-worker-count"
- className="form-control"
- min={0}
- value={this.state.siteForm.federation_worker_count}
- onInput={linkEvent(
- this,
- this.handleSiteFederationWorkerCount
- )}
- />
- </div>
- </div>
</>
)}
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<div className="form-check">
<input
className="form-check-label"
htmlFor="create-site-captcha-enabled"
>
- {i18n.t("captcha_enabled")}
+ {I18NextService.i18n.t("captcha_enabled")}
</label>
</div>
</div>
</div>
{this.state.siteForm.captcha_enabled && (
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<label
- className="form-check-label mr-2"
+ className="form-check-label me-2"
htmlFor="create-site-captcha-difficulty"
>
- {i18n.t("captcha_difficulty")}
+ {I18NextService.i18n.t("captcha_difficulty")}
</label>
<select
id="create-site-captcha-difficulty"
value={this.state.siteForm.captcha_difficulty}
onChange={linkEvent(this, this.handleSiteCaptchaDifficulty)}
- className="custom-select w-auto"
+ className="form-select d-inline-block w-auto"
>
- <option value="easy">{i18n.t("easy")}</option>
- <option value="medium">{i18n.t("medium")}</option>
- <option value="hard">{i18n.t("hard")}</option>
+ <option value="easy">{I18NextService.i18n.t("easy")}</option>
+ <option value="medium">
+ {I18NextService.i18n.t("medium")}
+ </option>
+ <option value="hard">{I18NextService.i18n.t("hard")}</option>
</select>
</div>
</div>
)}
- <div className="form-group row">
+ <div className="mb-3 row">
<div className="col-12">
<button
type="submit"
- className="btn btn-secondary mr-2"
- disabled={this.state.loading}
+ className="btn btn-secondary me-2"
+ disabled={this.props.loading}
>
- {this.state.loading ? (
+ {this.props.loading ? (
<Spinner />
) : siteSetup ? (
- capitalizeFirstLetter(i18n.t("save"))
+ capitalizeFirstLetter(I18NextService.i18n.t("save"))
) : (
- capitalizeFirstLetter(i18n.t("create"))
+ capitalizeFirstLetter(I18NextService.i18n.t("create"))
)}
</button>
</div>
);
}
+ componentDidUpdate(
+ prevProps: Readonly<{ children?: InfernoNode } & SiteFormProps>
+ ) {
+ if (
+ !(
+ deepEqual(prevProps.allowedInstances, this.props.allowedInstances) ||
+ deepEqual(prevProps.blockedInstances, this.props.blockedInstances)
+ )
+ ) {
+ this.setState({ siteForm: this.initSiteForm() });
+ }
+ }
+
federatedInstanceSelect(key: InstanceKey) {
const id = `create_site_${key}`;
const value = this.state.instance_select[key];
return (
<div className="col-12 col-md-6">
<label className="col-form-label" htmlFor={id}>
- {i18n.t(key)}
+ {I18NextService.i18n.t(key)}
</label>
<div className="d-flex justify-content-between align-items-center">
<input
/>
<button
type="button"
- className="btn btn-sm bg-success ml-2"
+ className="btn btn-sm bg-success ms-2"
onClick={linkEvent(key, this.handleAddInstance)}
style={"width: 2rem; height: 2rem;"}
tabIndex={
event.preventDefault();
const auth = myAuthRequired();
i.setState(s => ((s.siteForm.auth = auth), s));
- i.setState({ loading: true, submitted: true });
+ i.setState({ submitted: true });
const stateSiteForm = i.state.siteForm;
stateSiteForm.rate_limit_search_per_second,
federation_enabled: stateSiteForm.federation_enabled,
federation_debug: stateSiteForm.federation_debug,
- federation_worker_count: stateSiteForm.federation_worker_count,
captcha_enabled: stateSiteForm.captcha_enabled,
captcha_difficulty: stateSiteForm.captcha_difficulty,
allowed_instances: stateSiteForm.allowed_instances,
handleAddInstance(key: InstanceKey) {
const instance = this.state.instance_select[key].trim();
+
+ if (!validInstanceTLD(instance)) {
+ return;
+ }
+
if (!this.state.siteForm[key]?.includes(instance)) {
this.setState(s => ({
...s,
i.setState(i.state);
}
- handleSiteFederationWorkerCount(i: SiteForm, event: any) {
- i.setState(
- s => (
- (s.siteForm.federation_worker_count = Number(event.target.value)), s
- )
- );
- }
-
handleSiteCaptchaEnabled(i: SiteForm, event: any) {
i.state.siteForm.captcha_enabled = event.target.checked;
i.setState(i.state);