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 {sliderfy} from "../../ui/elements/Slider";
|
||||||
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
|
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
|
||||||
import { tr } from "tc-shared/i18n/localize";
|
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) {
|
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
|
let properties: ChannelProperties = { } as ChannelProperties; //The changes properties
|
||||||
const modal = createModal({
|
const modal = createModal({
|
||||||
header: channel ? tr("Edit channel") : tr("Create channel"),
|
header: channel ? tr("Edit channel") : tr("Create channel"),
|
||||||
|
|
|
@ -1,41 +1,94 @@
|
||||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
||||||
import {ChannelEntry} from "tc-shared/tree/Channel";
|
import {ChannelEntry, ChannelProperties} from "tc-shared/tree/Channel";
|
||||||
import {ChannelEditableProperty} from "tc-shared/ui/modal/channel-edit/Definitions";
|
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 {
|
class ChannelEditController {
|
||||||
private readonly connection: ConnectionHandler;
|
readonly uiEvents: Registry<ChannelEditEvents>;
|
||||||
private readonly channel: ChannelEntry;
|
|
||||||
|
|
||||||
constructor() {
|
private readonly listenerPermissions: (() => void)[];
|
||||||
this.getChannelProperty("sortingOrder");
|
|
||||||
|
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;
|
this.uiEvents.destroy();
|
||||||
|
}
|
||||||
/*
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
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 {
|
export interface ChannelEditableProperty {
|
||||||
"name": string,
|
"name": string,
|
||||||
"sortingOrder": { previousChannelId: number, availableChannels: { channelName: string, channelId: number }[] | undefined },
|
|
||||||
/*
|
|
||||||
"phoneticName": string,
|
"phoneticName": string,
|
||||||
"talkPower": number,
|
|
||||||
|
"type": "default" | "permanent" | "semi-permanent" | "temporary",
|
||||||
"password": { state: "set", password?: string } | { state: "clear" },
|
"password": { state: "set", password?: string } | { state: "clear" },
|
||||||
|
"sortingOrder": { previousChannelId: number, availableChannels: { channelName: string, channelId: number }[] | undefined },
|
||||||
|
|
||||||
"topic": string,
|
"topic": string,
|
||||||
"description": string,
|
"description": string,
|
||||||
"type": "default" | "permanent" | "semi-permanent" | "temporary",
|
|
||||||
|
"codec": { type: number, quality: number },
|
||||||
|
"talkPower": number,
|
||||||
|
"encryptedVoiceData": number
|
||||||
|
|
||||||
"maxUsers": "unlimited" | number,
|
"maxUsers": "unlimited" | number,
|
||||||
"maxFamilyUsers": "unlimited" | "inherited" | number,
|
"maxFamilyUsers": "unlimited" | "inherited" | number,
|
||||||
"codec": { type: number, quality: number },
|
|
||||||
"deleteDelay": number,
|
"deleteDelay": number,
|
||||||
"encryptedVoiceData": number
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChannelPropertyPermission {
|
export interface ChannelPropertyPermission {
|
||||||
|
@ -25,7 +28,7 @@ export interface ChannelPropertyPermission {
|
||||||
description: boolean,
|
description: boolean,
|
||||||
channelType: {
|
channelType: {
|
||||||
permanent: boolean,
|
permanent: boolean,
|
||||||
semipermanent: boolean,
|
semiPermanent: boolean,
|
||||||
temporary: boolean,
|
temporary: boolean,
|
||||||
default: boolean
|
default: boolean
|
||||||
},
|
},
|
||||||
|
@ -37,7 +40,7 @@ export interface ChannelPropertyPermission {
|
||||||
},
|
},
|
||||||
deleteDelay: {
|
deleteDelay: {
|
||||||
editable: boolean,
|
editable: boolean,
|
||||||
maxDelay: number,
|
maxDelay: number | -1,
|
||||||
},
|
},
|
||||||
encryptVoiceData: boolean
|
encryptVoiceData: boolean
|
||||||
}
|
}
|
||||||
|
@ -47,6 +50,17 @@ export interface ChannelPropertyStatus {
|
||||||
password: boolean
|
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 {
|
export interface ChannelEditEvents {
|
||||||
change_property: {
|
change_property: {
|
||||||
property: keyof ChannelEditableProperty
|
property: keyof ChannelEditableProperty
|
||||||
|
@ -60,12 +74,6 @@ export interface ChannelEditEvents {
|
||||||
permission: keyof ChannelPropertyPermission
|
permission: keyof ChannelPropertyPermission
|
||||||
}
|
}
|
||||||
|
|
||||||
notify_property: {
|
notify_property: ChannelEditPropertyEvent<keyof ChannelEditableProperty>,
|
||||||
property: keyof ChannelEditableProperty
|
notify_property_permission: ChannelEditPermissionEvent<keyof ChannelPropertyPermission>
|
||||||
value: ChannelEditableProperty[keyof ChannelEditableProperty]
|
|
||||||
},
|
|
||||||
notify_property_permission: {
|
|
||||||
permission: keyof ChannelPropertyPermission
|
|
||||||
value: ChannelPropertyPermission[keyof ChannelPropertyPermission]
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -2,25 +2,34 @@ import {InternalModal} from "tc-shared/ui/react-elements/internal-modal/Controll
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {Translatable} from "tc-shared/ui/react-elements/i18n";
|
import {Translatable} from "tc-shared/ui/react-elements/i18n";
|
||||||
import {Registry} from "tc-shared/events";
|
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 {useContext, useState} from "react";
|
||||||
import {BoxedInputField} from "tc-shared/ui/react-elements/InputField";
|
import {BoxedInputField} from "tc-shared/ui/react-elements/InputField";
|
||||||
|
|
||||||
const cssStyle = require("./Renderer.scss");
|
const cssStyle = require("./Renderer.scss");
|
||||||
|
|
||||||
|
const ModalTypeContext = React.createContext<"channel-edit" | "channel-create">("channel-edit");
|
||||||
const EventContext = React.createContext<Registry<ChannelEditEvents>>(undefined);
|
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) : {
|
const kPropertyLoading = "____loading_____";
|
||||||
originalValue: ChannelEditableProperty[T],
|
function useProperty<T extends keyof ChannelEditableProperty>(property: T) : ChannelPropertyState<T> {
|
||||||
currentValue: ChannelEditableProperty[T],
|
|
||||||
setCurrentValue: (value: ChannelEditableProperty[T]) => void
|
|
||||||
} | typeof kPropertyLoading {
|
|
||||||
const events = useContext(EventContext);
|
const events = useContext(EventContext);
|
||||||
|
|
||||||
const [ value, setValue ] = useState(() => {
|
const [ value, setValue ] = useState<ChannelEditableProperty[T] | typeof kPropertyLoading>(() => {
|
||||||
events.fire("query_property", { property: property });
|
events.fire("query_property", { property: property });
|
||||||
return kPropertyLoading;
|
return kPropertyLoading;
|
||||||
});
|
});
|
||||||
|
@ -33,21 +42,51 @@ function useProperty<T extends keyof ChannelEditableProperty>(property: T) : {
|
||||||
setValue(event.value as any);
|
setValue(event.value as any);
|
||||||
}, undefined, []);
|
}, 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 ChannelName = () => {
|
||||||
const changesApplying = useContext(ChangesApplying);
|
const modalType = useContext(ModalTypeContext);
|
||||||
const property = useProperty("name");
|
const { propertyValue, propertyState, setPropertyValue } = useProperty("name");
|
||||||
|
const editable = usePermission("name", modalType === "channel-create");
|
||||||
|
const [ edited, setEdited ] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BoxedInputField
|
<BoxedInputField
|
||||||
disabled={changesApplying || property === kPropertyLoading}
|
disabled={!editable || propertyState !== "normal"}
|
||||||
value={property === kPropertyLoading ? null : property.currentValue}
|
value={propertyValue}
|
||||||
placeholder={property === kPropertyLoading ? tr("loading") : tr("Channel name")}
|
placeholder={propertyState === "normal" ? tr("Channel name") : tr("loading")}
|
||||||
onInput={newValue => property !== kPropertyLoading && property.setCurrentValue(newValue)}
|
onInput={value => {
|
||||||
|
setPropertyValue(value);
|
||||||
|
setEdited(true);
|
||||||
|
}}
|
||||||
|
isInvalid={edited && (typeof propertyValue !== "string" || !propertyValue || propertyValue.length > 30)}
|
||||||
/>
|
/>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const GeneralContainer = () => {
|
const GeneralContainer = () => {
|
||||||
|
@ -59,12 +98,23 @@ const GeneralContainer = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChannelEditModal extends InternalModal {
|
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 {
|
renderBody(): React.ReactElement {
|
||||||
return (<>
|
return (
|
||||||
<GeneralContainer />
|
<EventContext.Provider value={this.events}>
|
||||||
</>);
|
<ModalTypeContext.Provider value={this.isChannelCreate ? "channel-create" : "channel-edit"}>
|
||||||
|
<GeneralContainer />
|
||||||
|
</ModalTypeContext.Provider>
|
||||||
|
</EventContext.Provider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
title(): string | React.ReactElement {
|
title(): string | React.ReactElement {
|
||||||
|
|
Loading…
Reference in New Issue