Merge branch 'develop'

# Conflicts:
#	shared/js/text/bbcode/renderer.ts
master
WolverinDEV 2020-12-21 20:26:59 +01:00
commit 943affff98
7 changed files with 443 additions and 72 deletions

View File

@ -1,16 +1,29 @@
import * as loader from "tc-loader";
import {Stage} from "tc-loader";
import * as React from "react";
import {Context} from "react";
import TextRenderer from "vendor/xbbcode/renderer/text";
import ReactRenderer from "vendor/xbbcode/renderer/react";
import HTMLRenderer from "vendor/xbbcode/renderer/html";
export const BBCodeHandlerContext = React.createContext<string>(undefined);
export const rendererText = new TextRenderer();
export const rendererReact = new ReactRenderer();
export const rendererHTML = new HTMLRenderer(rendererReact);
import "./emoji";
import "./highlight";
import "./youtube";
import "./url";
import "./image";
import * as React from "react";
export let BBCodeHandlerContext: Context<string>;
export const rendererText = new TextRenderer();
export const rendererReact = new ReactRenderer();
export const rendererHTML = new HTMLRenderer(rendererReact);
loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
name: "BBCode handler context",
function: async () => {
BBCodeHandlerContext = React.createContext<string>(undefined);
},
priority: 80
})

View File

@ -12,8 +12,12 @@ import {hashPassword} from "../../utils/helpers";
import {sliderfy} from "../../ui/elements/Slider";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
import { tr } from "tc-shared/i18n/localize";
import {spawnChannelEditNew} from "tc-shared/ui/modal/channel-edit/Controller";
export function createChannelModal(connection: ConnectionHandler, channel: ChannelEntry | undefined, parent: ChannelEntry | undefined, permissions: PermissionManager, callback: (properties?: ChannelProperties, permissions?: PermissionValue[]) => any) {
spawnChannelEditNew(connection, channel, parent, callback);
return;
let properties: ChannelProperties = { } as ChannelProperties; //The changes properties
const modal = createModal({
header: channel ? tr("Edit channel") : tr("Create channel"),

View File

@ -1,41 +1,94 @@
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
import {ChannelEntry} from "tc-shared/tree/Channel";
import {ChannelEditableProperty} from "tc-shared/ui/modal/channel-edit/Definitions";
import {ChannelEntry, ChannelProperties} from "tc-shared/tree/Channel";
import {ChannelEditEvents, ChannelPropertyPermission} from "tc-shared/ui/modal/channel-edit/Definitions";
import {Registry} from "tc-shared/events";
import {ChannelPropertyProviders} from "tc-shared/ui/modal/channel-edit/ControllerProperties";
import {LogCategory, logError} from "tc-shared/log";
import {ChannelPropertyPermissionsProviders} from "tc-shared/ui/modal/channel-edit/ControllerPermissions";
import {spawnReactModal} from "tc-shared/ui/react-elements/Modal";
import {ChannelEditModal} from "tc-shared/ui/modal/channel-edit/Renderer";
import {PermissionValue} from "tc-shared/permission/PermissionManager";
const spawnChannelEditNew = (connection: ConnectionHandler, channel: ChannelEntry) => {
export const spawnChannelEditNew = (connection: ConnectionHandler, channel: ChannelEntry | undefined, parent: ChannelEntry | undefined, callback: (properties?: ChannelProperties, permissions?: PermissionValue[]) => void) => {
const controller = new ChannelEditController(connection, channel);
const modal = spawnReactModal(ChannelEditModal, controller.uiEvents, typeof channel === "number");
modal.show().then(undefined);
modal.events.on("destroy", () => {
controller.destroy();
});
};
class ChannelEditController {
readonly uiEvents: Registry<ChannelEditEvents>;
private readonly listenerPermissions: (() => void)[];
private readonly connection: ConnectionHandler;
private readonly channel: ChannelEntry;
private readonly channel: ChannelEntry | undefined;
constructor() {
this.getChannelProperty("sortingOrder");
}
private readonly originalProperties: ChannelProperties;
private currentProperties: ChannelProperties;
getChannelProperty<T extends keyof ChannelEditableProperty>(property: T) : ChannelEditableProperty[T] {
constructor(connection: ConnectionHandler, channel: ChannelEntry | undefined) {
this.connection = connection;
this.channel = channel;
this.uiEvents = new Registry<ChannelEditEvents>();
const properties = this.channel.properties;
this.uiEvents.on("query_property", event => {
if (typeof ChannelPropertyProviders[event.property] !== "object") {
logError(LogCategory.CHANNEL, tr("Channel edit controller missing property provider %s."), event.property);
return;
}
/*
switch (property) {
case "name":
return properties.channel_name;
ChannelPropertyProviders[event.property as any].provider(this.currentProperties, this.channel, this.channel?.parent_channel(), this.connection.channelTree).then(value => {
this.uiEvents.fire_react("notify_property", {
property: event.property,
value: value
});
}).catch(error => {
logError(LogCategory.CHANNEL, tr("Failed to get property value for %s: %o"), event.property, error);
});
});
case "phoneticName":
return properties.channel_name_phonetic;
case "topic":
return properties.channel_topic;
case "description":
return properties.channel_description;
case "password":
break;
this.uiEvents.on("query_property_permission", event => this.notifyPropertyPermission(event.permission));
this.listenerPermissions = [];
for(const key of Object.keys(ChannelPropertyPermissionsProviders)) {
const provider = ChannelPropertyPermissionsProviders[key];
this.listenerPermissions.push(
...provider.registerUpdates(() => this.notifyPropertyPermission(key as any), this.connection.permissions, this.channel, this.connection.channelTree)
);
}
*/
return undefined;
if(channel) {
this.originalProperties = channel.properties;
} else {
this.originalProperties = new ChannelProperties();
}
/* FIXME: Correctly setup the currentProperties! */
this.currentProperties = new ChannelProperties();
}
}
destroy() {
this.listenerPermissions.forEach(callback => callback());
this.listenerPermissions.splice(0, this.listenerPermissions.length);
this.uiEvents.destroy();
}
private notifyPropertyPermission(permission: keyof ChannelPropertyPermission) {
if (typeof ChannelPropertyPermissionsProviders[permission] !== "object") {
logError(LogCategory.CHANNEL, tr("Channel edit controller missing property permission provider %s."), permission);
return;
}
const value = ChannelPropertyPermissionsProviders[permission].provider(this.connection.permissions, this.channel, this.connection.channelTree);
this.uiEvents.fire_react("notify_property_permission", {
permission: permission,
value: value
});
}
}

View File

@ -0,0 +1,92 @@
import {ChannelPropertyPermission} from "tc-shared/ui/modal/channel-edit/Definitions";
import {PermissionManager} from "tc-shared/permission/PermissionManager";
import {ChannelEntry} from "tc-shared/tree/Channel";
import {ChannelTree} from "tc-shared/tree/ChannelTree";
import PermissionType from "tc-shared/permission/PermissionType";
export type ChannelPropertyPermissionsProvider<T extends keyof ChannelPropertyPermission> = {
provider: (permissions: PermissionManager, channel: ChannelEntry | undefined, channelTree: ChannelTree) => ChannelPropertyPermission[T],
registerUpdates: (callback: () => void, permissions: PermissionManager, channel: ChannelEntry | undefined, channelTree: ChannelTree) => (() => void)[],
};
export const ChannelPropertyPermissionsProviders: {[T in keyof ChannelPropertyPermission]?: ChannelPropertyPermissionsProvider<T>} = {};
const SimplePermissionProvider = (createPermission: PermissionType, editPermission: PermissionType) => {
return {
provider: (permissions, channel) => {
return permissions.neededPermission(channel ? editPermission : createPermission).granted(1);
},
registerUpdates: (callback, permissions, channel) => [
permissions.register_needed_permission(channel ? editPermission : createPermission, callback)
]
};
}
ChannelPropertyPermissionsProviders["name"] = {
provider: (permissions, channel) => {
return channel ? permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_NAME).granted(1) : true;
},
registerUpdates: (callback, permissions) => [
permissions.register_needed_permission(PermissionType.B_CHANNEL_MODIFY_NAME, callback)
]
}
ChannelPropertyPermissionsProviders["sortingOrder"] = SimplePermissionProvider(PermissionType.B_CHANNEL_CREATE_WITH_SORTORDER, PermissionType.B_CHANNEL_MODIFY_SORTORDER);
ChannelPropertyPermissionsProviders["description"] = SimplePermissionProvider(PermissionType.B_CHANNEL_CREATE_WITH_DESCRIPTION, PermissionType.B_CHANNEL_MODIFY_DESCRIPTION);
ChannelPropertyPermissionsProviders["topic"] = SimplePermissionProvider(PermissionType.B_CHANNEL_CREATE_WITH_TOPIC, PermissionType.B_CHANNEL_MODIFY_TOPIC);
ChannelPropertyPermissionsProviders["maxUsers"] = SimplePermissionProvider(PermissionType.B_CHANNEL_CREATE_WITH_MAXCLIENTS, PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS);
ChannelPropertyPermissionsProviders["maxFamilyUsers"] = SimplePermissionProvider(PermissionType.B_CHANNEL_CREATE_WITH_MAXFAMILYCLIENTS, PermissionType.B_CHANNEL_MODIFY_MAXFAMILYCLIENTS);
ChannelPropertyPermissionsProviders["talkPower"] = SimplePermissionProvider(PermissionType.B_CHANNEL_CREATE_WITH_NEEDED_TALK_POWER, PermissionType.B_CHANNEL_MODIFY_NEEDED_TALK_POWER);
ChannelPropertyPermissionsProviders["encryptVoiceData"] = SimplePermissionProvider(PermissionType.B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED, PermissionType.B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED);
ChannelPropertyPermissionsProviders["password"] = {
provider: (permissions, channel) => {
return {
editable: permissions.neededPermission(channel ? PermissionType.B_CHANNEL_MODIFY_PASSWORD : PermissionType.B_CHANNEL_CREATE_WITH_PASSWORD).granted(1),
enforced: permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD).granted(1),
};
},
registerUpdates: (callback, permissions, channel) => [
permissions.register_needed_permission(channel ? PermissionType.B_CHANNEL_MODIFY_PASSWORD : PermissionType.B_CHANNEL_CREATE_WITH_PASSWORD, callback),
permissions.register_needed_permission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_FORCE_PASSWORD, callback)
]
};
ChannelPropertyPermissionsProviders["channelType"] = {
provider: (permissions, channel) => {
return {
permanent: permissions.neededPermission(channel ? PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT : PermissionType.B_CHANNEL_CREATE_PERMANENT).granted(1),
semiPermanent: permissions.neededPermission(channel ? PermissionType.B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT : PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT).granted(1),
temporary: permissions.neededPermission(channel ? PermissionType.B_CHANNEL_MODIFY_MAKE_TEMPORARY : PermissionType.B_CHANNEL_CREATE_TEMPORARY).granted(1),
default: permissions.neededPermission(channel ? PermissionType.B_CHANNEL_MODIFY_MAKE_DEFAULT : PermissionType.B_CHANNEL_CREATE_WITH_DEFAULT).granted(1),
};
},
registerUpdates: (callback, permissions, channel) => [
permissions.register_needed_permission(channel ? PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT : PermissionType.B_CHANNEL_CREATE_PERMANENT, callback),
permissions.register_needed_permission(channel ? PermissionType.B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT : PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT, callback),
permissions.register_needed_permission(channel ? PermissionType.B_CHANNEL_MODIFY_MAKE_TEMPORARY : PermissionType.B_CHANNEL_CREATE_TEMPORARY, callback),
permissions.register_needed_permission(channel ? PermissionType.B_CHANNEL_MODIFY_MAKE_DEFAULT : PermissionType.B_CHANNEL_CREATE_WITH_DEFAULT, callback),
]
};
ChannelPropertyPermissionsProviders["codec"] = {
provider: (permissions) => {
return {
opusMusic: permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC).granted(1),
opusVoice: permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE).granted(1),
};
},
registerUpdates: (callback, permissions) => [
permissions.register_needed_permission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSMUSIC, callback),
permissions.register_needed_permission(PermissionType.B_CHANNEL_CREATE_MODIFY_WITH_CODEC_OPUSVOICE, callback),
]
};
ChannelPropertyPermissionsProviders["deleteDelay"] = {
provider: permissions => {
return {
editable: permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_TEMP_DELETE_DELAY).granted(1),
maxDelay: permissions.neededPermission(PermissionType.I_CHANNEL_CREATE_MODIFY_WITH_TEMP_DELETE_DELAY).valueNormalOr(-1)
}
},
registerUpdates: (callback, permissions) => [
permissions.register_needed_permission(PermissionType.B_CHANNEL_MODIFY_TEMP_DELETE_DELAY, callback),
permissions.register_needed_permission(PermissionType.I_CHANNEL_CREATE_MODIFY_WITH_TEMP_DELETE_DELAY, callback),
]
};

View File

@ -0,0 +1,151 @@
import {ChannelEntry, ChannelProperties} from "tc-shared/tree/Channel";
import {ChannelEditableProperty} from "tc-shared/ui/modal/channel-edit/Definitions";
import {ChannelTree} from "tc-shared/tree/ChannelTree";
export type ChannelPropertyProvider<T extends keyof ChannelEditableProperty> = {
provider: (properties: ChannelProperties, channel: ChannelEntry | undefined, parentChannel: ChannelEntry | undefined, channelTree: ChannelTree) => Promise<ChannelEditableProperty[T]>,
applier: (value: ChannelEditableProperty[T], properties: Partial<ChannelProperties>, channel: ChannelEntry | undefined) => void
};
export const ChannelPropertyProviders: {[T in keyof ChannelEditableProperty]?: ChannelPropertyProvider<T>} = {};
const SimplePropertyProvider = <P extends keyof ChannelProperties>(channelProperty: P, defaultValue: ChannelProperties[P]) => {
return {
provider: async properties => typeof properties[channelProperty] === "undefined" ? defaultValue : properties[channelProperty],
applier: (value, properties) => properties[channelProperty] = value
};
}
ChannelPropertyProviders["name"] = SimplePropertyProvider("channel_name", "");
ChannelPropertyProviders["phoneticName"] = SimplePropertyProvider("channel_name_phonetic", "");
ChannelPropertyProviders["type"] = {
provider: async properties => {
if(properties.channel_flag_default) {
return "default";
} else if(properties.channel_flag_permanent) {
return "permanent";
} else if(properties.channel_flag_semi_permanent) {
return "semi-permanent";
} else {
return "temporary";
}
},
applier: (value, properties) => {
properties["channel_flag_default"] = false;
properties["channel_flag_permanent"] = false;
properties["channel_flag_semi_permanent"] = false;
switch (value) {
case "default":
properties["channel_flag_permanent"] = true;
properties["channel_flag_default"] = true;
break;
case "permanent":
properties["channel_flag_permanent"] = true;
break;
case "semi-permanent":
properties["channel_flag_semi_permanent"] = true;
break;
case "temporary":
/* Nothing to do, default state */
}
}
}
ChannelPropertyProviders["password"] = {
provider: async properties => properties.channel_flag_password ? { state: "set" } : { state: "clear" },
applier: (value, properties) => {
if(value.state === "set") {
properties.channel_flag_password = true;
/* FIXME: Hash the password! */
properties.channel_password = value.password;
} else {
properties.channel_flag_password = false;
}
}
}
ChannelPropertyProviders["sortingOrder"] = {
provider: async (properties, channel, parentChannel, channelTree) => {
const availableChannels: { channelName: string, channelId: number }[] = [];
let channelSibling: ChannelEntry = parentChannel ? parentChannel.child_channel_head : channelTree.get_first_channel();
while(channelSibling) {
availableChannels.push({ channelId: channelSibling.channelId, channelName: channelSibling.properties.channel_name });
channelSibling = channelSibling.channel_next;
}
return {
previousChannelId: typeof properties.channel_order === "undefined" ? 0 : properties.channel_order,
availableChannels: availableChannels
}
},
applier: (value, properties) => properties.channel_order = value.previousChannelId
};
ChannelPropertyProviders["topic"] = SimplePropertyProvider("channel_topic", "");
ChannelPropertyProviders["description"] = {
provider: async (properties, channel) => {
if(channel) {
return await channel.getChannelDescription();
} else {
return "";
}
},
applier: (value, properties) => properties.channel_description = value
};
ChannelPropertyProviders["codec"] = {
provider: async properties => {
return {
type: properties.channel_codec,
quality: properties.channel_codec_quality
};
},
applier: (value, properties) => {
properties.channel_codec = value.type;
properties.channel_codec_quality = value.quality;
}
}
ChannelPropertyProviders["talkPower"] = SimplePropertyProvider("channel_needed_talk_power", 0);
ChannelPropertyProviders["encryptedVoiceData"] = SimplePropertyProvider("channel_codec_is_unencrypted", true);
ChannelPropertyProviders["maxUsers"] = {
provider: async properties => {
if(properties.channel_flag_maxclients_unlimited) {
return "unlimited";
}
return properties.channel_maxclients;
},
applier: (value, properties) => {
if(value === "unlimited") {
properties.channel_flag_maxclients_unlimited = true;
} else {
properties.channel_flag_maxclients_unlimited = false;
properties.channel_maxclients = value;
}
}
}
ChannelPropertyProviders["maxFamilyUsers"] = {
provider: async (properties) => {
if(properties.channel_flag_maxfamilyclients_unlimited) {
return "unlimited";
} else if(properties.channel_flag_maxfamilyclients_inherited) {
return "inherited";
} else {
return properties.channel_maxfamilyclients;
}
},
applier: (value, properties) => {
if(value === "unlimited") {
properties.channel_flag_maxfamilyclients_unlimited = true;
properties.channel_flag_maxfamilyclients_inherited = false;
} else if(value === "inherited") {
properties.channel_flag_maxfamilyclients_unlimited = false;
properties.channel_flag_maxfamilyclients_inherited = true;
} else {
properties.channel_flag_maxfamilyclients_unlimited = false;
properties.channel_flag_maxfamilyclients_inherited = false;
properties.channel_maxfamilyclients = value;
}
}
}
ChannelPropertyProviders["deleteDelay"] = SimplePropertyProvider("channel_delete_delay", 60);

View File

@ -1,19 +1,22 @@
export interface ChannelEditableProperty {
"name": string,
"sortingOrder": { previousChannelId: number, availableChannels: { channelName: string, channelId: number }[] | undefined },
/*
"phoneticName": string,
"talkPower": number,
"type": "default" | "permanent" | "semi-permanent" | "temporary",
"password": { state: "set", password?: string } | { state: "clear" },
"sortingOrder": { previousChannelId: number, availableChannels: { channelName: string, channelId: number }[] | undefined },
"topic": string,
"description": string,
"type": "default" | "permanent" | "semi-permanent" | "temporary",
"codec": { type: number, quality: number },
"talkPower": number,
"encryptedVoiceData": number
"maxUsers": "unlimited" | number,
"maxFamilyUsers": "unlimited" | "inherited" | number,
"codec": { type: number, quality: number },
"deleteDelay": number,
"encryptedVoiceData": number
*/
}
export interface ChannelPropertyPermission {
@ -25,7 +28,7 @@ export interface ChannelPropertyPermission {
description: boolean,
channelType: {
permanent: boolean,
semipermanent: boolean,
semiPermanent: boolean,
temporary: boolean,
default: boolean
},
@ -37,7 +40,7 @@ export interface ChannelPropertyPermission {
},
deleteDelay: {
editable: boolean,
maxDelay: number,
maxDelay: number | -1,
},
encryptVoiceData: boolean
}
@ -47,6 +50,17 @@ export interface ChannelPropertyStatus {
password: boolean
}
export type ChannelEditPropertyEvent<T extends keyof ChannelEditableProperty> = {
property: T,
value: ChannelEditableProperty[T]
}
export type ChannelEditPermissionEvent<T extends keyof ChannelPropertyPermission> = {
permission: T,
value: ChannelPropertyPermission[T]
}
export interface ChannelEditEvents {
change_property: {
property: keyof ChannelEditableProperty
@ -60,12 +74,6 @@ export interface ChannelEditEvents {
permission: keyof ChannelPropertyPermission
}
notify_property: {
property: keyof ChannelEditableProperty
value: ChannelEditableProperty[keyof ChannelEditableProperty]
},
notify_property_permission: {
permission: keyof ChannelPropertyPermission
value: ChannelPropertyPermission[keyof ChannelPropertyPermission]
}
notify_property: ChannelEditPropertyEvent<keyof ChannelEditableProperty>,
notify_property_permission: ChannelEditPermissionEvent<keyof ChannelPropertyPermission>
}

View File

@ -2,25 +2,34 @@ import {InternalModal} from "tc-shared/ui/react-elements/internal-modal/Controll
import * as React from "react";
import {Translatable} from "tc-shared/ui/react-elements/i18n";
import {Registry} from "tc-shared/events";
import {ChannelEditableProperty, ChannelEditEvents} from "tc-shared/ui/modal/channel-edit/Definitions";
import {
ChannelEditableProperty,
ChannelEditEvents,
ChannelPropertyPermission
} from "tc-shared/ui/modal/channel-edit/Definitions";
import {useContext, useState} from "react";
import {BoxedInputField} from "tc-shared/ui/react-elements/InputField";
const cssStyle = require("./Renderer.scss");
const ModalTypeContext = React.createContext<"channel-edit" | "channel-create">("channel-edit");
const EventContext = React.createContext<Registry<ChannelEditEvents>>(undefined);
const ChangesApplying = React.createContext(false);
const kPropertyLoading = "loading";
type ChannelPropertyState<T extends keyof ChannelEditableProperty> = {
setPropertyValue: (value: ChannelEditableProperty[T]) => void
} & ({
propertyState: "loading",
propertyValue: undefined,
} | {
propertyState: "normal" | "applying",
propertyValue: ChannelEditableProperty[T],
})
function useProperty<T extends keyof ChannelEditableProperty>(property: T) : {
originalValue: ChannelEditableProperty[T],
currentValue: ChannelEditableProperty[T],
setCurrentValue: (value: ChannelEditableProperty[T]) => void
} | typeof kPropertyLoading {
const kPropertyLoading = "____loading_____";
function useProperty<T extends keyof ChannelEditableProperty>(property: T) : ChannelPropertyState<T> {
const events = useContext(EventContext);
const [ value, setValue ] = useState(() => {
const [ value, setValue ] = useState<ChannelEditableProperty[T] | typeof kPropertyLoading>(() => {
events.fire("query_property", { property: property });
return kPropertyLoading;
});
@ -33,21 +42,51 @@ function useProperty<T extends keyof ChannelEditableProperty>(property: T) : {
setValue(event.value as any);
}, undefined, []);
return kPropertyLoading;
if(value === kPropertyLoading) {
return {
propertyState: "loading",
propertyValue: undefined,
setPropertyValue: _value => {}
};
} else {
return {
propertyState: "normal",
propertyValue: value,
setPropertyValue: setValue as any
};
}
}
function usePermission<T extends keyof ChannelPropertyPermission>(permission: T, defaultValue: ChannelPropertyPermission[T]) : ChannelPropertyPermission[T] {
const events = useContext(EventContext);
const [ value, setValue ] = useState<ChannelPropertyPermission[T]>(() => {
events.fire("query_property_permission", { permission: permission });
return defaultValue;
});
events.reactUse("notify_property_permission", event => event.permission === permission && setValue(event.value as any));
return value;
}
const ChannelName = () => {
const changesApplying = useContext(ChangesApplying);
const property = useProperty("name");
const modalType = useContext(ModalTypeContext);
const { propertyValue, propertyState, setPropertyValue } = useProperty("name");
const editable = usePermission("name", modalType === "channel-create");
const [ edited, setEdited ] = useState(false);
return (
<BoxedInputField
disabled={changesApplying || property === kPropertyLoading}
value={property === kPropertyLoading ? null : property.currentValue}
placeholder={property === kPropertyLoading ? tr("loading") : tr("Channel name")}
onInput={newValue => property !== kPropertyLoading && property.setCurrentValue(newValue)}
disabled={!editable || propertyState !== "normal"}
value={propertyValue}
placeholder={propertyState === "normal" ? tr("Channel name") : tr("loading")}
onInput={value => {
setPropertyValue(value);
setEdited(true);
}}
isInvalid={edited && (typeof propertyValue !== "string" || !propertyValue || propertyValue.length > 30)}
/>
)
);
}
const GeneralContainer = () => {
@ -59,12 +98,23 @@ const GeneralContainer = () => {
}
export class ChannelEditModal extends InternalModal {
private readonly channelExists: number;
private readonly events: Registry<ChannelEditEvents>;
private readonly isChannelCreate: boolean;
constructor(events: Registry<ChannelEditEvents>, isChannelCreate: boolean) {
super();
this.events = events;
this.isChannelCreate = isChannelCreate;
}
renderBody(): React.ReactElement {
return (<>
<GeneralContainer />
</>);
return (
<EventContext.Provider value={this.events}>
<ModalTypeContext.Provider value={this.isChannelCreate ? "channel-create" : "channel-edit"}>
<GeneralContainer />
</ModalTypeContext.Provider>
</EventContext.Provider>
);
}
title(): string | React.ReactElement {