import {ChannelEntry, ChannelNameParser, ChannelProperties, ChannelSidebarMode} from "tc-shared/tree/Channel"; import {ChannelEditableProperty} from "tc-shared/ui/modal/channel-edit/Definitions"; import {ChannelTree} from "tc-shared/tree/ChannelTree"; import {ServerFeature} from "tc-shared/connection/ServerFeatures"; 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"] = { provider: async (properties, channel, parentChannel, channelTree) => { let spacerUniqueId = 0; const hasParent = !!(channel?.hasParent() || parentChannel); if(!hasParent) { const channels = channelTree.rootChannel(); while(true) { let matchFound = false; for(const channel of channels) { if(channel.parsed_channel_name.uniqueId === spacerUniqueId.toString()) { matchFound = true; break; } } if(!matchFound) { break; } spacerUniqueId++; } } const parsed = new ChannelNameParser(properties.channel_name, hasParent); return { rawName: properties.channel_name, spacerUniqueId: parsed.uniqueId || spacerUniqueId.toString(), hasParent, maxNameLength: 30 - (parsed.originalName.length - parsed.text.length), parsedAlignment: parsed.alignment, parsedName: parsed.text } }, applier: (value, properties) => properties.channel_name = value.rawName } ChannelPropertyProviders["phoneticName"] = SimplePropertyProvider("channel_name_phonetic", ""); ChannelPropertyProviders["icon"] = { provider: async (properties, _channel, _parentChannel, channelTree) => { return { iconId: properties.channel_icon_id, remoteIcon: { iconId: properties.channel_icon_id, serverUniqueId: channelTree.server.properties.virtualserver_unique_identifier, handlerId: channelTree.client.handlerId } } }, applier: (value, properties) => properties.channel_icon_id = value.iconId }; ChannelPropertyProviders["type"] = { provider: async (properties, channel) => { let type; if(properties.channel_flag_default) { type = "default"; } else if(properties.channel_flag_permanent) { type = "permanent"; } else if(properties.channel_flag_semi_permanent) { type = "semi-permanent"; } else { type = "temporary"; } return { type: type, originallyDefault: !!channel?.properties.channel_flag_default }; }, applier: (value, properties) => { properties["channel_flag_default"] = false; properties["channel_flag_permanent"] = false; properties["channel_flag_semi_permanent"] = false; switch (value.type) { 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["sideBar"] = { provider: async (properties, channel, parentChannel, channelTree) => { const features = channelTree.client.serverFeatures; if(!features.supportsFeature(ServerFeature.SIDEBAR_MODE)) { return "not-supported"; } switch (properties.channel_sidebar_mode) { case ChannelSidebarMode.FileTransfer: return "file-transfer"; case ChannelSidebarMode.Description: return "description"; case ChannelSidebarMode.Conversation: return "conversation"; case ChannelSidebarMode.Unknown: default: return "unknown"; } }, applier: (value, properties) => { switch (value) { case "file-transfer": properties.channel_sidebar_mode = ChannelSidebarMode.FileTransfer; break; case "conversation": properties.channel_sidebar_mode = ChannelSidebarMode.Conversation; break; case "description": properties.channel_sidebar_mode = ChannelSidebarMode.Description; break; } } } 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) => { const description = await channel.getChannelDescription(false); if(description.status === "success") { return description.description; } 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"] = { provider: async (properties, channel, parentChannel, channelTree) => { let serverSetting: "encrypted" | "unencrypted" | "individual"; switch (channelTree.server.properties.virtualserver_codec_encryption_mode) { case 1: serverSetting = "unencrypted"; break; case 2: serverSetting = "encrypted"; break; default: serverSetting = "individual"; break; } return { encrypted: properties.channel_codec_is_unencrypted, serverSetting: serverSetting }; }, applier: (value, properties) => properties.channel_codec_is_unencrypted = value.encrypted } 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);