From de052d566be02067bd569830cc741da8f722d1ee Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Mon, 21 Dec 2020 20:25:07 +0100 Subject: [PATCH] Tem changes for the new channel create modal --- shared/js/ui/modal/ModalCreateChannel.ts | 4 + shared/js/ui/modal/channel-edit/Controller.ts | 107 +++++++++---- .../channel-edit/ControllerPermissions.ts | 92 +++++++++++ .../channel-edit/ControllerProperties.ts | 151 ++++++++++++++++++ .../js/ui/modal/channel-edit/Definitions.ts | 42 +++-- shared/js/ui/modal/channel-edit/Renderer.tsx | 92 ++++++++--- 6 files changed, 423 insertions(+), 65 deletions(-) create mode 100644 shared/js/ui/modal/channel-edit/ControllerPermissions.ts create mode 100644 shared/js/ui/modal/channel-edit/ControllerProperties.ts diff --git a/shared/js/ui/modal/ModalCreateChannel.ts b/shared/js/ui/modal/ModalCreateChannel.ts index cd19b86f..47a916f2 100644 --- a/shared/js/ui/modal/ModalCreateChannel.ts +++ b/shared/js/ui/modal/ModalCreateChannel.ts @@ -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"), diff --git a/shared/js/ui/modal/channel-edit/Controller.ts b/shared/js/ui/modal/channel-edit/Controller.ts index e3613df3..46ddcc06 100644 --- a/shared/js/ui/modal/channel-edit/Controller.ts +++ b/shared/js/ui/modal/channel-edit/Controller.ts @@ -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; + + 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(property: T) : ChannelEditableProperty[T] { + constructor(connection: ConnectionHandler, channel: ChannelEntry | undefined) { + this.connection = connection; + this.channel = channel; + this.uiEvents = new Registry(); - 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(); } -} \ No newline at end of file + + 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 + }); + } +} diff --git a/shared/js/ui/modal/channel-edit/ControllerPermissions.ts b/shared/js/ui/modal/channel-edit/ControllerPermissions.ts new file mode 100644 index 00000000..07624d08 --- /dev/null +++ b/shared/js/ui/modal/channel-edit/ControllerPermissions.ts @@ -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 = { + 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} = {}; + +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), + ] +}; \ No newline at end of file diff --git a/shared/js/ui/modal/channel-edit/ControllerProperties.ts b/shared/js/ui/modal/channel-edit/ControllerProperties.ts new file mode 100644 index 00000000..bda13038 --- /dev/null +++ b/shared/js/ui/modal/channel-edit/ControllerProperties.ts @@ -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 = { + provider: (properties: ChannelProperties, channel: ChannelEntry | undefined, parentChannel: ChannelEntry | undefined, channelTree: ChannelTree) => Promise, + applier: (value: ChannelEditableProperty[T], properties: Partial, channel: ChannelEntry | undefined) => void +}; + +export const ChannelPropertyProviders: {[T in keyof ChannelEditableProperty]?: ChannelPropertyProvider} = {}; + +const SimplePropertyProvider =

(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); \ No newline at end of file diff --git a/shared/js/ui/modal/channel-edit/Definitions.ts b/shared/js/ui/modal/channel-edit/Definitions.ts index 08a1a3fe..e507c53a 100644 --- a/shared/js/ui/modal/channel-edit/Definitions.ts +++ b/shared/js/ui/modal/channel-edit/Definitions.ts @@ -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 = { + property: T, + value: ChannelEditableProperty[T] +} + + +export type ChannelEditPermissionEvent = { + 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, + notify_property_permission: ChannelEditPermissionEvent } \ No newline at end of file diff --git a/shared/js/ui/modal/channel-edit/Renderer.tsx b/shared/js/ui/modal/channel-edit/Renderer.tsx index c5eb31a3..617af70f 100644 --- a/shared/js/ui/modal/channel-edit/Renderer.tsx +++ b/shared/js/ui/modal/channel-edit/Renderer.tsx @@ -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>(undefined); -const ChangesApplying = React.createContext(false); -const kPropertyLoading = "loading"; +type ChannelPropertyState = { + setPropertyValue: (value: ChannelEditableProperty[T]) => void +} & ({ + propertyState: "loading", + propertyValue: undefined, +} | { + propertyState: "normal" | "applying", + propertyValue: ChannelEditableProperty[T], +}) -function useProperty(property: T) : { - originalValue: ChannelEditableProperty[T], - currentValue: ChannelEditableProperty[T], - setCurrentValue: (value: ChannelEditableProperty[T]) => void -} | typeof kPropertyLoading { +const kPropertyLoading = "____loading_____"; +function useProperty(property: T) : ChannelPropertyState { const events = useContext(EventContext); - const [ value, setValue ] = useState(() => { + const [ value, setValue ] = useState(() => { events.fire("query_property", { property: property }); return kPropertyLoading; }); @@ -33,21 +42,51 @@ function useProperty(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(permission: T, defaultValue: ChannelPropertyPermission[T]) : ChannelPropertyPermission[T] { + const events = useContext(EventContext); + const [ value, setValue ] = useState(() => { + 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 ( 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; + private readonly isChannelCreate: boolean; + + constructor(events: Registry, isChannelCreate: boolean) { + super(); + this.events = events; + this.isChannelCreate = isChannelCreate; + } renderBody(): React.ReactElement { - return (<> - - ); + return ( + + + + + + ); } title(): string | React.ReactElement {