Tem changes for the new channel create modal
parent
65406447f5
commit
de052d566b
|
@ -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"),
|
||||
|
|
|
@ -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 {
|
||||
private readonly connection: ConnectionHandler;
|
||||
private readonly channel: ChannelEntry;
|
||||
readonly uiEvents: Registry<ChannelEditEvents>;
|
||||
|
||||
constructor() {
|
||||
this.getChannelProperty("sortingOrder");
|
||||
private readonly listenerPermissions: (() => void)[];
|
||||
|
||||
private readonly connection: ConnectionHandler;
|
||||
private readonly channel: ChannelEntry | undefined;
|
||||
|
||||
private readonly originalProperties: ChannelProperties;
|
||||
private currentProperties: ChannelProperties;
|
||||
|
||||
constructor(connection: ConnectionHandler, channel: ChannelEntry | undefined) {
|
||||
this.connection = connection;
|
||||
this.channel = channel;
|
||||
this.uiEvents = new Registry<ChannelEditEvents>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
if(channel) {
|
||||
this.originalProperties = channel.properties;
|
||||
} else {
|
||||
this.originalProperties = new ChannelProperties();
|
||||
}
|
||||
|
||||
/* FIXME: Correctly setup the currentProperties! */
|
||||
this.currentProperties = new ChannelProperties();
|
||||
}
|
||||
|
||||
getChannelProperty<T extends keyof ChannelEditableProperty>(property: T) : ChannelEditableProperty[T] {
|
||||
destroy() {
|
||||
this.listenerPermissions.forEach(callback => callback());
|
||||
this.listenerPermissions.splice(0, this.listenerPermissions.length);
|
||||
|
||||
const properties = this.channel.properties;
|
||||
|
||||
/*
|
||||
switch (property) {
|
||||
case "name":
|
||||
return properties.channel_name;
|
||||
|
||||
case "phoneticName":
|
||||
return properties.channel_name_phonetic;
|
||||
|
||||
case "topic":
|
||||
return properties.channel_topic;
|
||||
|
||||
case "description":
|
||||
return properties.channel_description;
|
||||
|
||||
case "password":
|
||||
break;
|
||||
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;
|
||||
}
|
||||
*/
|
||||
return undefined;
|
||||
|
||||
const value = ChannelPropertyPermissionsProviders[permission].provider(this.connection.permissions, this.channel, this.connection.channelTree);
|
||||
this.uiEvents.fire_react("notify_property_permission", {
|
||||
permission: permission,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
]
|
||||
};
|
|
@ -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);
|
|
@ -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>
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue