<symbol id="icon-refresh-cw" viewBox="0 0 24 24">
<path d="M4.453 9.334c0.737-2.083 2.247-3.669 4.096-4.552s4.032-1.059 6.114-0.322c1.186 0.42 2.206 1.088 2.983 1.88l2.83 2.66h-3.476c-0.552 0-1 0.448-1 1s0.448 1 1 1h5.997c0.005 0 0.009 0 0.014 0 0.137-0.001 0.268-0.031 0.386-0.082 0.119-0.051 0.229-0.126 0.324-0.225 0.012-0.013 0.024-0.026 0.036-0.039 0.075-0.087 0.133-0.183 0.173-0.285s0.064-0.211 0.069-0.326c0.001-0.015 0.001-0.029 0.001-0.043v-6c0-0.552-0.448-1-1-1s-1 0.448-1 1v3.689l-2.926-2.749c-0.992-1.010-2.271-1.843-3.743-2.364-2.603-0.921-5.335-0.699-7.643 0.402s-4.199 3.086-5.12 5.689c-0.185 0.52 0.088 1.091 0.608 1.276s1.092-0.088 1.276-0.609zM2 16.312l2.955 2.777c1.929 1.931 4.49 2.908 7.048 2.909s5.119-0.975 7.072-2.927c1.104-1.104 1.901-2.407 2.361-3.745 0.18-0.522-0.098-1.091-0.621-1.271s-1.091 0.098-1.271 0.621c-0.361 1.050-0.993 2.091-1.883 2.981-1.563 1.562-3.609 2.342-5.657 2.342s-4.094-0.782-5.679-2.366l-2.8-2.633h3.475c0.552 0 1-0.448 1-1s-0.448-1-1-1h-5.997c-0.005 0-0.009 0-0.014 0-0.137 0.001-0.268 0.031-0.386 0.082-0.119 0.051-0.229 0.126-0.324 0.225-0.012 0.013-0.024 0.026-0.036 0.039-0.075 0.087-0.133 0.183-0.173 0.285s-0.064 0.211-0.069 0.326c-0.001 0.015-0.001 0.029-0.001 0.043v6c0 0.552 0.448 1 1 1s1-0.448 1-1z"></path>
</symbol>
+ <symbol id="icon-add" viewBox="0 0 32 32" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
+ <path d="M14.5,14.501l-10.502,0c-0.828,0 -1.5,0.673 -1.5,1.5c0,0.828 0.672,1.5 1.5,1.5l10.502,0l-0.001,10.502c0,0.828 0.672,1.5 1.5,1.501c0.828,-0 1.5,-0.673 1.5,-1.5l0.001,-10.503l10.502,0c0.828,0 1.5,-0.672 1.5,-1.5c0,-0.827 -0.672,-1.5 -1.5,-1.5l-10.502,0l0.001,-10.501c-0,-0.828 -0.672,-1.501 -1.5,-1.501c-0.828,0 -1.5,0.672 -1.5,1.5l-0.001,10.502Z"/>
+ </symbol>
<symbol id="icon-play" viewBox="0 0 24 24">
<path d="M5.541 2.159c-0.153-0.1-0.34-0.159-0.541-0.159-0.552 0-1 0.448-1 1v18c-0.001 0.182 0.050 0.372 0.159 0.541 0.299 0.465 0.917 0.599 1.382 0.3l14-9c0.114-0.072 0.219-0.174 0.3-0.3 0.299-0.465 0.164-1.083-0.3-1.382zM6 4.832l11.151 7.168-11.151 7.168z"></path>
</symbol>
-import { Component, InfernoMouseEvent, linkEvent } from "inferno";
+import {
+ Component,
+ InfernoKeyboardEvent,
+ InfernoMouseEvent,
+ linkEvent,
+} from "inferno";
import { Prompt } from "inferno-router";
import {
CreateSite,
myAuth,
wsClient,
} from "../../utils";
-import { Spinner } from "../common/icon";
+import { Icon, Spinner } from "../common/icon";
import { ImageUploadForm } from "../common/image-upload-form";
import { LanguageSelect } from "../common/language-select";
import { ListingTypeSelect } from "../common/listing-type-select";
siteForm: EditSite;
loading: boolean;
themeList?: string[];
+ instance_select: {
+ allowed_instances: string;
+ blocked_instances: string;
+ };
}
+type InstanceKey = "allowed_instances" | "blocked_instances";
+
export class SiteForm extends Component<SiteFormProps, SiteFormState> {
state: SiteFormState = {
siteForm: {
auth: "TODO",
},
loading: false,
+ instance_select: {
+ allowed_instances: "",
+ blocked_instances: "",
+ },
};
constructor(props: any, context: any) {
{this.state.siteForm.federation_enabled && (
<>
<div className="form-group row">
- <label
- className="col-12 col-form-label"
- htmlFor="create-site-allowed-instances"
- >
- {i18n.t("allowed_instances")}
- </label>
- <div className="col-12">
- <input
- type="text"
- placeholder="instance1.tld,instance2.tld"
- id="create-site-allowed-instances"
- className="form-control"
- value={this.instancesToString(
- this.state.siteForm.allowed_instances
- )}
- onInput={linkEvent(this, this.handleSiteAllowedInstances)}
- />
- </div>
- </div>
- <div className="form-group row">
- <label
- className="col-12 col-form-label"
- htmlFor="create-site-blocked-instances"
- >
- {i18n.t("blocked_instances")}
- </label>
- <div className="col-12">
- <input
- type="text"
- placeholder="instance1.tld,instance2.tld"
- id="create-site-blocked-instances"
- className="form-control"
- value={this.instancesToString(
- this.state.siteForm.blocked_instances
- )}
- onInput={linkEvent(this, this.handleSiteBlockedInstances)}
- />
- </div>
+ {this.federatedInstanceSelect("allowed_instances")}
+ {this.federatedInstanceSelect("blocked_instances")}
</div>
<div className="form-group row">
<div className="col-12">
);
}
+ federatedInstanceSelect(key: InstanceKey) {
+ const id = `create_site_${key}`;
+ const value = this.state.instance_select[key];
+ const selectedInstances = this.state.siteForm[key];
+ return (
+ <div className="col-12 col-md-6">
+ <label className="col-form-label" htmlFor={id}>
+ {i18n.t(key)}
+ </label>
+ <div className="d-flex justify-content-between align-items-center">
+ <input
+ type="text"
+ placeholder="instance.tld"
+ id={id}
+ className="form-control"
+ value={value}
+ onInput={linkEvent(key, this.handleInstanceTextChange)}
+ onKeyUp={linkEvent(key, this.handleInstanceEnterPress)}
+ />
+ <button
+ type="button"
+ className="btn btn-sm bg-success ml-2"
+ onClick={linkEvent(key, this.handleAddInstance)}
+ tabIndex={
+ -1 /* Making this untabble because handling enter key in text input makes keyboard support for this button redundant */
+ }
+ >
+ <Icon icon="add" classes="icon-inline text-light m-auto" />
+ </button>
+ </div>
+ {selectedInstances && selectedInstances.length > 0 && (
+ <ul className="mt-3 list-unstyled w-100 d-flex flex-column justify-content-around align-items-center">
+ {selectedInstances.map(instance => (
+ <li
+ key={instance}
+ className="my-1 w-100 w-md-75 d-flex align-items-center justify-content-between"
+ >
+ <label className="d-block m-0 w-100 " htmlFor={instance}>
+ <strong>{instance}</strong>
+ </label>
+ <button
+ id={instance}
+ type="button"
+ className="btn btn-sm bg-danger"
+ onClick={linkEvent(
+ { key, instance },
+ this.handleRemoveInstance
+ )}
+ >
+ <Icon icon="x" classes="icon-inline text-light m-auto" />
+ </button>
+ </li>
+ ))}
+ </ul>
+ )}
+ </div>
+ );
+ }
+
+ handleInstanceTextChange(type: InstanceKey, event: any) {
+ this.setState(s => ({
+ ...s,
+ instance_select: {
+ ...s.instance_select,
+ [type]: event.target.value,
+ },
+ }));
+ }
+
+ handleInstanceEnterPress(
+ key: InstanceKey,
+ event: InfernoKeyboardEvent<HTMLInputElement>
+ ) {
+ if (event.code.toLowerCase() === "enter") {
+ event.preventDefault();
+
+ this.handleAddInstance(key);
+ }
+ }
+
handleCreateSiteSubmit(i: SiteForm, event: any) {
event.preventDefault();
i.setState({ loading: true });
i.setState(i.state);
}
- instancesToString(opt?: string[]): string {
- return opt ? opt.join(",") : "";
- }
-
- handleSiteAllowedInstances(i: SiteForm, event: any) {
- let list = splitToList(event.target.value);
- i.setState(s => ((s.siteForm.allowed_instances = list), s));
+ handleAddInstance(key: InstanceKey) {
+ const instance = this.state.instance_select[key].trim();
+ if (!this.state.siteForm[key]?.includes(instance)) {
+ this.setState(s => ({
+ ...s,
+ siteForm: {
+ ...s.siteForm,
+ [key]: [...(s.siteForm[key] ?? []), instance],
+ },
+ instance_select: {
+ ...s.instance_select,
+ [key]: "",
+ },
+ }));
+
+ const oppositeKey: InstanceKey =
+ key === "allowed_instances" ? "blocked_instances" : "allowed_instances";
+ if (this.state.siteForm[oppositeKey]?.includes(instance)) {
+ this.handleRemoveInstance({ key: oppositeKey, instance });
+ }
+ }
}
- handleSiteBlockedInstances(i: SiteForm, event: any) {
- let list = splitToList(event.target.value);
- i.setState(s => ((s.siteForm.blocked_instances = list), s));
+ handleRemoveInstance({
+ key,
+ instance,
+ }: {
+ key: InstanceKey;
+ instance: string;
+ }) {
+ this.setState(s => ({
+ ...s,
+ siteForm: {
+ ...s.siteForm,
+ [key]: s.siteForm[key]?.filter(i => i !== instance),
+ },
+ }));
}
handleSiteNameChange(i: SiteForm, event: any) {
this.setState(s => ((s.siteForm.default_post_listing_type = val), s));
}
}
-
-function splitToList(commaList: string): string[] {
- if (commaList !== "") {
- let list = commaList.trim().split(",");
- return list;
- } else {
- return [];
- }
-}