Adjusted to the new icon API

canary
WolverinDEV 2020-09-26 01:22:21 +02:00
parent ca4216be67
commit 9782bb355a
27 changed files with 185 additions and 147 deletions

View File

@ -1159,6 +1159,10 @@ export class ConnectionHandler {
this.getVoiceRecorder()?.input?.setFilterMode(FilterMode.Filter);
this.update_voice_status();
}
getCurrentServerUniqueId() {
return this.channelTree.server.properties.virtualserver_unique_identifier;
}
}
export type ConnectionStateUpdateType = "microphone" | "speaker" | "away" | "subscribe" | "query";

View File

@ -43,6 +43,8 @@ import {ConnectRequestData} from "tc-shared/ipc/ConnectHandler";
import "./video-viewer/Controller";
import "./profiles/ConnectionProfile";
import "./update/UpdaterWeb";
import "./file/LocalIcons";
import {defaultConnectProfile, findConnectProfile} from "tc-shared/profiles/ConnectionProfile";
import {server_connections} from "tc-shared/ConnectionManager";
import {initializeConnectionUIList} from "tc-shared/ui/frames/connection-handler-list/Controller";

View File

@ -740,8 +740,10 @@ export class ChannelEntry extends ChannelTreeEntry<ChannelEvents> {
}
set collapsed(flag: boolean) {
if(this._flag_collapsed === flag)
if(this._flag_collapsed === flag) {
return;
}
this._flag_collapsed = flag;
this.events.fire("notify_collapsed_state_changed", { collapsed: flag });
this.view.current?.forceUpdate();

View File

@ -20,10 +20,11 @@ import {spawnAbout} from "../../ui/modal/ModalAbout";
import * as loader from "tc-loader";
import {formatMessage} from "../../ui/frames/chat";
import {control_bar_instance} from "../../ui/frames/control-bar";
import {icon_cache_loader, IconManager, LocalIcon} from "../../file/Icons";
import {icon_cache_loader, IconManager, LocalIcon} from "../../file/IconsOld";
import {spawnPermissionEditorModal} from "../../ui/modal/permission/ModalPermissionEditor";
import {global_client_actions} from "tc-shared/events/GlobalEvents";
import {server_connections} from "tc-shared/ConnectionManager";
import {generateIconJQueryTag, getIconManager, RemoteIcon} from "tc-shared/file/Icons";
export interface HRItem { }
@ -33,7 +34,7 @@ export interface MenuItem {
delete_item(item: MenuItem | HRItem);
items() : (MenuItem | HRItem)[];
icon(klass?: string | LocalIcon) : string; //FIXME: Native client must work as well!
icon(klass?: string | RemoteIcon) : string;
label(value?: string) : string;
visible(value?: boolean) : boolean;
disabled(value?: boolean) : boolean;
@ -179,12 +180,13 @@ namespace html {
return this;
}
icon(klass?: string | LocalIcon): string {
icon(klass?: string | RemoteIcon): string {
this._label_icon_tag.children().remove();
if(typeof(klass) === "string")
if(typeof(klass) === "string") {
$.spawn("div").addClass("icon_em " + klass).appendTo(this._label_icon_tag);
else
IconManager.generate_tag(klass).appendTo(this._label_icon_tag);
} else {
generateIconJQueryTag(klass).appendTo(this._label_icon_tag);
}
return "";
}
@ -290,7 +292,7 @@ export function rebuild_bookmarks() {
const bookmark = entry as Bookmark;
const item = root.append_item(bookmark.display_name);
item.icon(icon_cache_loader.load_icon(bookmark.last_icon_id, bookmark.last_icon_server_id));
item.icon(getIconManager().resolveIcon(bookmark.last_icon_id, bookmark.last_icon_server_id));
item.click(() => boorkmak_connect(bookmark));
}
};

View File

@ -9,6 +9,7 @@ import {ClientInfo} from "../../ui/frames/side/client_info";
import {MusicInfo} from "../../ui/frames/side/music_info";
import {ConversationManager} from "../../ui/frames/side/ConversationManager";
import {PrivateConversationManager} from "../../ui/frames/side/PrivateConversationManager";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
export enum InfoFrameMode {
NONE = "none",
@ -126,8 +127,10 @@ export class InfoFrame {
html_tag.children().remove();
if(channel) {
if(channel.properties.channel_icon_id != 0)
client.handle.fileManager.icons.generateTag(channel.properties.channel_icon_id).appendTo(html_tag);
if(channel.properties.channel_icon_id != 0) {
const connection = channel.channelTree.client;
generateIconJQueryTag(getIconManager().resolveIcon(channel.properties.channel_icon_id, connection.getCurrentServerUniqueId(), connection.handlerId)).appendTo(html_tag);
}
$.spawn("div").text(channel.formattedChannelName()).appendTo(html_tag);
this.update_channel_limit(channel, html_limit_tag);
@ -154,8 +157,10 @@ export class InfoFrame {
/* initialize */
if(channel) {
if(channel.properties.channel_icon_id != 0)
this.handle.handle.fileManager.icons.generateTag(channel.properties.channel_icon_id).appendTo(html_tag);
if(channel.properties.channel_icon_id != 0) {
const connection = channel.channelTree.client;
generateIconJQueryTag(getIconManager().resolveIcon(channel.properties.channel_icon_id, connection.getCurrentServerUniqueId(), connection.handlerId)).appendTo(html_tag);
}
$.spawn("div").text(channel.formattedChannelName()).appendTo(html_tag);
this.update_channel_limit(channel, html_limit_tag);
@ -163,8 +168,10 @@ export class InfoFrame {
html_tag.append(formatMessage(tr("Unknown channel id {}"), current_channel_id));
} else if(channel_tree && current_channel_id == 0) {
const server = this.handle.handle.channelTree.server;
if(server.properties.virtualserver_icon_id != 0)
this.handle.handle.fileManager.icons.generateTag(server.properties.virtualserver_icon_id).appendTo(html_tag);
if(server.properties.virtualserver_icon_id != 0) {
const connection = server.channelTree.client;
generateIconJQueryTag(getIconManager().resolveIcon(server.properties.virtualserver_icon_id, connection.getCurrentServerUniqueId(), connection.handlerId)).appendTo(html_tag);
}
$.spawn("div").text(server.properties.virtualserver_name).appendTo(html_tag);
html_tag_title.text(tr("You're chatting in Server"));

View File

@ -6,7 +6,8 @@ import {ConnectionHandlerList} from "tc-shared/ui/frames/connection-handler-list
import {server_connections} from "tc-shared/ConnectionManager";
import {LogCategory, logWarn} from "tc-shared/log";
import {ConnectionState} from "tc-shared/ConnectionHandler";
import {LocalIcon} from "tc-shared/file/Icons";
import {LocalIcon} from "../../../file/IconsOld";
import {RemoteIconInfo} from "tc-shared/file/Icons";
export function initializeConnectionUIList() {
const container = document.getElementById("connection-handler-list");
@ -119,19 +120,13 @@ function initializeController(events: Registry<ConnectionListUIEvents>) {
break;
}
let icon: LocalIcon | undefined;
let iconId = handler.channelTree.server.properties.virtualserver_icon_id;
if(iconId !== 0) {
icon = handler.fileManager.icons.load_icon(handler.channelTree.server.properties.virtualserver_icon_id);
}
events.fire_async("notify_handler_status", {
handlerId: event.handlerId,
status: {
handlerName: handler.channelTree.server.properties.virtualserver_name,
connectionState: state,
voiceReplaying: handler.getServerConnection().getVoiceConnection().isReplayingVoice(),
serverIcon: icon
serverIcon: { iconId: handler.channelTree.server.properties.virtualserver_icon_id, serverUniqueId: handler.getCurrentServerUniqueId() }
}
});
});

View File

@ -1,4 +1,4 @@
import {LocalIcon} from "tc-shared/file/Icons";
import {RemoteIconInfo} from "tc-shared/file/Icons";
export type MouseMoveCoordinates = { x: number, y: number, xOffset: number };
export type HandlerConnectionState = "disconnected" | "connecting" | "connected";
@ -7,7 +7,7 @@ export type HandlerStatus = {
connectionState: HandlerConnectionState,
handlerName: string,
voiceReplaying: boolean,
serverIcon: LocalIcon | undefined
serverIcon: RemoteIconInfo | undefined
}
export interface ConnectionListUIEvents {

View File

@ -6,13 +6,14 @@ import {
} from "tc-shared/ui/frames/connection-handler-list/Definitions";
import * as React from "react";
import {useContext, useEffect, useRef, useState} from "react";
import {IconRenderer, LocalIconRenderer} from "tc-shared/ui/react-elements/Icon";
import {IconRenderer, LocalIconRenderer, RemoteIconRenderer} from "tc-shared/ui/react-elements/Icon";
import {ClientIcon} from "svg-sprites/client-icons";
import {Translatable} from "tc-shared/ui/react-elements/i18n";
import {LoadingDots} from "tc-shared/ui/react-elements/LoadingDots";
import ResizeObserver from 'resize-observer-polyfill';
import {LogCategory, logWarn} from "tc-shared/log";
import {ClientIconRenderer} from "tc-shared/ui/react-elements/Icons";
import {getIconManager} from "tc-shared/file/Icons";
const cssStyle = require("./Renderer.scss");
const Events = React.createContext<Registry<ConnectionListUIEvents>>(undefined);
@ -46,9 +47,7 @@ const ConnectionHandler = React.memo((props: { handlerId: string, mode: Connecti
cutoffName = status.handlerName.length > 30;
voiceReplaying = status.voiceReplaying;
displayedName = <React.Fragment key={"connected"}>{status.handlerName}</React.Fragment>;
if(status.serverIcon) {
icon = <LocalIconRenderer icon={status.serverIcon} key={"server-icon"} />;
}
icon = <RemoteIconRenderer icon={getIconManager().resolveIcon(status.serverIcon.iconId, status.serverIcon.serverUniqueId, props.handlerId)} />;
break;
case "connecting":

View File

@ -1,11 +1,11 @@
import * as React from "react";
import {ReactComponentBase} from "tc-shared/ui/react-elements/ReactComponentBase";
import {IconRenderer} from "tc-shared/ui/react-elements/Icon";
import {LocalIcon} from "tc-shared/file/Icons";
import {IconRenderer, RemoteIconRenderer} from "tc-shared/ui/react-elements/Icon";
import {getIconManager, RemoteIconInfo} from "tc-shared/file/Icons";
const cssStyle = require("./button.scss");
export interface DropdownEntryProperties {
icon?: string | LocalIcon;
icon?: string | RemoteIconInfo;
text: JSX.Element | string;
onClick?: (event) => void;
@ -19,7 +19,9 @@ export class DropdownEntry extends ReactComponentBase<DropdownEntryProperties, {
if(this.props.children) {
return (
<div className={cssStyle.dropdownEntry} onClick={this.props.onClick} onContextMenu={this.props.onContextMenu}>
<IconRenderer icon={this.props.icon} />
{typeof this.props.icon === "string" ? <IconRenderer icon={this.props.icon} /> :
<RemoteIconRenderer icon={getIconManager().resolveIcon(this.props.icon.iconId, this.props.icon.serverUniqueId)} />
}
<a className={cssStyle.entryName}>{this.props.text}</a>
<div className={this.classList("arrow", "right")} />
<DropdownContainer>
@ -30,7 +32,9 @@ export class DropdownEntry extends ReactComponentBase<DropdownEntryProperties, {
} else {
return (
<div className={cssStyle.dropdownEntry} onClick={this.props.onClick} onContextMenu={this.props.onContextMenu}>
<IconRenderer icon={this.props.icon} />
{typeof this.props.icon === "string" ? <IconRenderer icon={this.props.icon} /> :
<RemoteIconRenderer icon={getIconManager().resolveIcon(this.props.icon.iconId, this.props.icon.serverUniqueId)} />
}
<a className={cssStyle.entryName}>{this.props.text}</a>
</div>
);

View File

@ -22,7 +22,6 @@ import {
import * as contextmenu from "tc-shared/ui/elements/ContextMenu";
import {createInputModal} from "tc-shared/ui/elements/Modal";
import {global_client_actions} from "tc-shared/events/GlobalEvents";
import {icon_cache_loader} from "tc-shared/file/Icons";
import {ConnectionManagerEvents, server_connections} from "tc-shared/ConnectionManager";
const cssStyle = require("./index.scss");
@ -121,7 +120,7 @@ class BookmarkButton extends ReactComponentBase<{ event_registry: Registry<Inter
private renderBookmark(bookmark: Bookmark) {
return (
<DropdownEntry key={bookmark.unique_id}
icon={icon_cache_loader.load_icon(bookmark.last_icon_id, bookmark.last_icon_server_id)}
icon={{ iconId: bookmark.last_icon_id, serverUniqueId: bookmark.last_icon_server_id }}
text={bookmark.display_name}
onClick={BookmarkButton.onBookmarkClick.bind(undefined, bookmark.unique_id)}
onContextMenu={this.onBookmarkContextMenu.bind(this, bookmark.unique_id)}/>

View File

@ -10,6 +10,7 @@ import {findLogDispatcher} from "../../../ui/frames/log/DispatcherLog";
import {formatDate} from "../../../MessageFormatter";
import {Settings, settings} from "../../../settings";
import {server_connections} from "tc-shared/ConnectionManager";
import {getIconManager} from "tc-shared/file/Icons";
export type DispatcherLog<T extends keyof TypeInfo> = (data: TypeInfo[T], handlerId: string, eventType: T) => void;
@ -58,9 +59,12 @@ async function resolveServerIconUrl(handlerId: string) {
const connection = server_connections.findConnection(handlerId);
if(connection.channelTree.server.properties.virtualserver_icon_id) {
const icon = connection.fileManager.icons.load_icon(connection.channelTree.server.properties.virtualserver_icon_id);
await icon.await_loading();
return icon.loaded_url;
const icon = getIconManager().resolveIcon(connection.channelTree.server.properties.virtualserver_icon_id, connection.getCurrentServerUniqueId(), connection.handlerId);
await icon.awaitLoaded();
if(icon.getState() === "loaded" && icon.iconId > 1000) {
return icon.getImageUrl();
}
}
return kDefaultIcon;

View File

@ -6,6 +6,7 @@ import * as image_preview from "../image_preview";
import * as i18nc from "../../../i18n/country";
import {ClientEntry, LocalClientEntry} from "../../../tree/Client";
import {format_online_time} from "../../../utils/TimeUtils";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
export class ClientInfo {
readonly handle: Frame;
@ -246,7 +247,7 @@ export class ClientInfo {
container_groups.append(
$.spawn("div").addClass("group-container")
.append(
this.handle.handle.fileManager.icons.generateTag(group.properties.iconid)
generateIconJQueryTag(getIconManager().resolveIcon(group.properties.iconid, this.handle.handle.getCurrentServerUniqueId(), this.handle.handle.handlerId))
).append(
$.spawn("a").text(group.name).attr("title", tr("Group id: ") + group.id)
)
@ -265,7 +266,7 @@ export class ClientInfo {
container_group.append(
$.spawn("div").addClass("group-container")
.append(
this.handle.handle.fileManager.icons.generateTag(group.properties.iconid)
generateIconJQueryTag(getIconManager().resolveIcon(group.properties.iconid, this.handle.handle.getCurrentServerUniqueId(), this.handle.handle.handlerId))
).append(
$.spawn("a").text(group.name).attr("title", tr("Group id: ") + group_id)
)

View File

@ -20,7 +20,8 @@ import * as i18nc from "../../i18n/country";
import {formatMessage} from "../../ui/frames/chat";
import * as top_menu from "../frames/MenuBar";
import {control_bar_instance} from "../../ui/frames/control-bar";
import {icon_cache_loader, IconManager} from "../../file/Icons";
import {icon_cache_loader, IconManager} from "../../file/IconsOld";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
export function spawnBookmarkModal() {
let modal: Modal;
@ -153,8 +154,8 @@ export function spawnBookmarkModal() {
if (entry.type === BookmarkType.ENTRY) {
const bookmark = entry as Bookmark;
container.append(
bookmark.last_icon_id ?
IconManager.generate_tag(icon_cache_loader.load_icon(bookmark.last_icon_id, bookmark.last_icon_server_id), {animate: false}) :
bookmark.last_icon_id && bookmark.last_icon_server_id ?
generateIconJQueryTag(getIconManager().resolveIcon(bookmark.last_icon_id, bookmark.last_icon_server_id), {animate: false}) :
$.spawn("div").addClass("icon-container icon_em")
);
} else {

View File

@ -6,6 +6,7 @@ import * as i18nc from "../../i18n/country";
import * as tooltip from "../../ui/elements/Tooltip";
import * as moment from "moment";
import {format_number, network} from "../../ui/frames/chat";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
type InfoUpdateCallback = (info: ClientConnectionInfo) => any;
@ -311,8 +312,9 @@ function apply_groups(client: ClientEntry, tag: JQuery, modal: Modal, callbacks:
if (!group) continue; //This shall never happen!
container_empty.hide();
container_entries.append($.spawn("div").addClass("entry").append(
client.channelTree.client.fileManager.icons.generateTag(group.properties.iconid),
generateIconJQueryTag(getIconManager().resolveIcon(group.properties.iconid, client.channelTree.client.getCurrentServerUniqueId(), client.channelTree.client.handlerId)),
$.spawn("a").addClass("name").text(group.name + " (" + group.id + ")"),
$.spawn("div").addClass("button-delete").append(
$.spawn("div").addClass("icon_em client-delete").attr("title", tr("Delete group")).on('click', event => {

View File

@ -7,8 +7,9 @@ import {ConnectionProfile, defaultConnectProfile, findConnectProfile, availableC
import {KeyCode} from "../../PPTListener";
import * as i18nc from "../../i18n/country";
import {spawnSettingsModal} from "../../ui/modal/ModalSettings";
import {icon_cache_loader, IconManager} from "../../file/Icons";
import {icon_cache_loader, IconManager} from "../../file/IconsOld";
import {server_connections} from "tc-shared/ConnectionManager";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
//FIXME: Move this shit out of this file!
export namespace connection_log {
@ -290,6 +291,7 @@ export function spawnConnectModal(options: {
/* connect history show */
{
for (const entry of connection_log.history().slice(0, 10)) {
$.spawn("div").addClass("row").append(
$.spawn("div").addClass("column delete").append($.spawn("div").addClass("icon_em client-delete")).on('click', event => {
@ -304,7 +306,7 @@ export function spawnConnectModal(options: {
})
).append(
$.spawn("div").addClass("column name").append([
IconManager.generate_tag(icon_cache_loader.load_icon(entry.icon_id, entry.server_unique_id)),
generateIconJQueryTag(getIconManager().resolveIcon(entry.icon_id, entry.server_unique_id), {animate: false}),
$.spawn("a").text(entry.name)
])
).append(

View File

@ -10,6 +10,7 @@ import * as tooltip from "../../ui/elements/Tooltip";
import {spawnIconSelect} from "../../ui/modal/ModalIconSelect";
import {hashPassword} from "../../utils/helpers";
import {sliderfy} from "../../ui/elements/Slider";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
export function createChannelModal(connection: ConnectionHandler, channel: ChannelEntry | undefined, parent: ChannelEntry | undefined, permissions: PermissionManager, callback: (properties?: ChannelProperties, permissions?: PermissionValue[]) => any) {
let properties: ChannelProperties = { } as ChannelProperties; //The changes properties
@ -21,8 +22,9 @@ export function createChannelModal(connection: ConnectionHandler, channel: Chann
channel_flag_maxfamilyclients_unlimited: true,
channel_flag_maxclients_unlimited: true,
});
render_properties["channel_icon_tab"] = connection.fileManager.icons.generateTag(channel ? channel.properties.channel_icon_id : 0);
render_properties["channel_icon_general"] = connection.fileManager.icons.generateTag(channel ? channel.properties.channel_icon_id : 0);
render_properties["channel_icon_tab"] = generateIconJQueryTag(getIconManager().resolveIcon(channel ? channel.properties.channel_icon_id : 0, connection.getCurrentServerUniqueId(), connection.handlerId));
render_properties["channel_icon_general"] = generateIconJQueryTag(getIconManager().resolveIcon(channel ? channel.properties.channel_icon_id : 0, connection.getCurrentServerUniqueId(), connection.handlerId));
render_properties["create"] = !channel;
let template = $("#tmpl_channel_edit").renderTag(render_properties);
@ -134,7 +136,7 @@ function applyGeneralListener(connection: ConnectionHandler, properties: Channel
spawnIconSelect(connection, id => {
const icon_node = tag.find(".icon-preview");
icon_node.children().remove();
icon_node.append(connection.fileManager.icons.generateTag(id));
icon_node.append(generateIconJQueryTag(getIconManager().resolveIcon(id, connection.getCurrentServerUniqueId(), connection.handlerId)));
console.log("Selected icon ID: %d", id);
properties.channel_icon_id = id;
@ -144,7 +146,7 @@ function applyGeneralListener(connection: ConnectionHandler, properties: Channel
tag.find(".button-icon-remove").on('click', event => {
const icon_node = tag.find(".icon-preview");
icon_node.children().remove();
icon_node.append(connection.fileManager.icons.generateTag(0));
icon_node.append(generateIconJQueryTag(getIconManager().resolveIcon(0, connection.getCurrentServerUniqueId(), connection.handlerId)));
console.log("Remove channel icon");
properties.channel_icon_id = 0;

View File

@ -4,6 +4,7 @@ import * as log from "../../log";
import {ClientEntry} from "../../tree/Client";
import {GroupManager, GroupType} from "../../permission/GroupManager";
import PermissionType from "../../permission/PermissionType";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
let current_modal: Modal;
export function createServerGroupAssignmentModal(client: ClientEntry, callback: (groups: number[], flag: boolean) => Promise<boolean>) {
@ -27,7 +28,7 @@ export function createServerGroupAssignmentModal(client: ClientEntry, callback:
entry["name"] = group.name;
entry["disabled"] = !client.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_ADD_POWER).granted(group.requiredMemberRemovePower);
entry["default"] = client.channelTree.server.properties.virtualserver_default_server_group == group.id;
tag["icon_" + group.id] = client.channelTree.client.fileManager.icons.generateTag(group.properties.iconid);
tag["icon_" + group.id] = generateIconJQueryTag(getIconManager().resolveIcon(group.properties.iconid, client.channelTree.server.properties.virtualserver_unique_identifier, client.channelTree.client.handlerId));
groups.push(entry);
}

View File

@ -10,6 +10,7 @@ import * as crc32 from "../../crypto/crc32";
import {FileInfo} from "../../file/FileManager";
import {FileTransferState, TransferProvider} from "../../file/Transfer";
import {ErrorCode} from "../../connection/ErrorCode";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id: number) => any, selected_icon?: number) {
selected_icon = selected_icon || 0;
@ -49,22 +50,24 @@ export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id:
const update_local_icons = (icons: number[]) => {
container_icons_local.empty();
for (const icon_id of icons) {
const tag = client.fileManager.icons.generateTag(icon_id, {animate: false}).attr('title', "Icon " + icon_id);
for (const iconId of icons) {
const iconTag = generateIconJQueryTag(getIconManager().resolveIcon(iconId, client.channelTree.server.properties.virtualserver_unique_identifier), { animate: false });
const tag = iconTag.attr('title', "Icon " + iconId);
if (callback_icon) {
tag.on('click', event => {
container_icons.find(".selected").removeClass("selected");
tag.addClass("selected");
selected_container.empty().append(tag.clone());
selected_icon = icon_id;
selected_icon = iconId;
button_select.prop("disabled", false);
});
tag.on('dblclick', event => {
callback_icon(icon_id);
callback_icon(iconId);
modal.close();
});
if (icon_id == selected_icon)
if (iconId == selected_icon)
tag.trigger('click');
}
tag.appendTo(container_icons_local);
@ -101,19 +104,21 @@ export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id:
if (!chunk) return;
for (const icon of chunk) {
const icon_id = parseInt(icon.name.substr("icon_".length));
if (Number.isNaN(icon_id)) {
const iconId = parseInt(icon.name.substr("icon_".length));
if (Number.isNaN(iconId)) {
log.warn(LogCategory.GENERAL, tr("Received an unparsable icon within icon list (%o)"), icon);
continue;
}
const tag = client.fileManager.icons.generateTag(icon_id, {animate: false}).attr('title', "Icon " + icon_id);
const iconTag = generateIconJQueryTag(getIconManager().resolveIcon(iconId, client.channelTree.server.properties.virtualserver_unique_identifier), { animate: false });
const tag = iconTag.attr('title', "Icon " + iconId);
if (callback_icon || allow_manage) {
tag.on('click', event => {
container_icons.find(".selected").removeClass("selected");
tag.addClass("selected");
selected_container.empty().append(tag.clone());
selected_icon = icon_id;
selected_icon = iconId;
button_select.prop("disabled", false);
button_delete.prop("disabled", !allow_manage);
});
@ -121,10 +126,10 @@ export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id:
if (!callback_icon)
return;
callback_icon(icon_id);
callback_icon(iconId);
modal.close();
});
if (icon_id == selected_icon)
if (iconId == selected_icon)
tag.trigger('click');
}
tag.appendTo(container_icons_remote);
@ -158,7 +163,7 @@ export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id:
if (selected_icon < 1000) return; /* we cant delete local icons */
client.fileManager.icons.delete_icon(selected_icon).then(() => {
client.fileManager.deleteIcon(selected_icon).then(() => {
selected.detach();
}).catch(error => {
if (error instanceof CommandResult && error.id == ErrorCode.SERVER_INSUFFICIENT_PERMISSIONS)

View File

@ -6,6 +6,7 @@ import {hashPassword} from "../../utils/helpers";
import * as tooltip from "../../ui/elements/Tooltip";
import {spawnIconSelect} from "../../ui/modal/ModalIconSelect";
import {network} from "../../ui/frames/chat";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
export function createServerModal(server: ServerEntry, callback: (properties?: ServerProperties) => Promise<void>) {
const properties = Object.assign({}, server.properties);
@ -43,7 +44,7 @@ export function createServerModal(server: ServerEntry, callback: (properties?: S
header: tr("Manage the Virtual Server"),
body: () => {
const template = $("#tmpl_server_edit").renderTag(Object.assign(Object.assign({}, server.properties), {
server_icon: server.channelTree.client.fileManager.icons.generateTag(server.properties.virtualserver_icon_id)
server_icon: generateIconJQueryTag(getIconManager().resolveIcon(server.properties.virtualserver_icon_id, server.properties.virtualserver_unique_identifier, server.channelTree.client.handlerId))
}));
/* the tab functionality */
@ -120,7 +121,7 @@ function apply_general_listener(tag: JQuery, server: ServerEntry, properties: Se
spawnIconSelect(server.channelTree.client, id => {
const icon_node = tag.find(".icon-preview");
icon_node.children().remove();
icon_node.append(server.channelTree.client.fileManager.icons.generateTag(id));
icon_node.append(generateIconJQueryTag(getIconManager().resolveIcon(id, server.properties.virtualserver_unique_identifier, server.channelTree.client.handlerId)));
console.log("Selected icon ID: %d", id);
properties.virtualserver_icon_id = id;
@ -131,7 +132,7 @@ function apply_general_listener(tag: JQuery, server: ServerEntry, properties: Se
tag.find(".button-icon-remove").on('click', event => {
const icon_node = tag.find(".icon-preview");
icon_node.children().remove();
icon_node.append(server.channelTree.client.fileManager.icons.generateTag(0));
icon_node.append(generateIconJQueryTag(getIconManager().resolveIcon(0, server.properties.virtualserver_unique_identifier)));
console.log("Remove server icon");
properties.virtualserver_icon_id = 0;

View File

@ -11,11 +11,12 @@ import {LogCategory} from "tc-shared/log";
import ResizeObserver from "resize-observer-polyfill";
import {LoadingDots} from "tc-shared/ui/react-elements/LoadingDots";
import {Button} from "tc-shared/ui/react-elements/Button";
import {IconRenderer, LocalIconRenderer} from "tc-shared/ui/react-elements/Icon";
import {IconRenderer, LocalIconRenderer, RemoteIconRenderer} from "tc-shared/ui/react-elements/Icon";
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
import * as contextmenu from "tc-shared/ui/elements/ContextMenu";
import {copy_to_clipboard} from "tc-shared/utils/helpers";
import {createInfoModal} from "tc-shared/ui/elements/Modal";
import {getIconManager} from "tc-shared/file/Icons";
const cssStyle = require("./PermissionEditor.scss");
@ -165,8 +166,9 @@ const ButtonIconPreview = (props: { events: Registry<PermissionEditorEvents>, co
});
let icon;
if (!unset && iconId > 0)
icon = <LocalIconRenderer key={"icon-" + iconId} icon={props.connection.fileManager.icons.load_icon(iconId)}/>;
if (!unset && iconId > 0) {
icon = <RemoteIconRenderer key={"icon-" + iconId} icon={getIconManager().resolveIcon(iconId, props.connection.getCurrentServerUniqueId(), props.connection.handlerId)} />;
}
return (
<div className={cssStyle.containerIconSelect}>

View File

@ -4,7 +4,7 @@ import {EventHandler, ReactEventHandler, Registry} from "tc-shared/events";
import {ChannelInfo, GroupProperties, PermissionModalEvents} from "tc-shared/ui/modal/permission/ModalPermissionEditor";
import {PermissionEditorEvents} from "tc-shared/ui/modal/permission/PermissionEditor";
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
import {LocalIconRenderer} from "tc-shared/ui/react-elements/Icon";
import {LocalIconRenderer, RemoteIconRenderer} from "tc-shared/ui/react-elements/Icon";
import {createInputModal} from "tc-shared/ui/elements/Modal";
import {Translatable} from "tc-shared/ui/react-elements/i18n";
import {LoadingDots} from "tc-shared/ui/react-elements/LoadingDots";
@ -14,6 +14,7 @@ import {copy_to_clipboard} from "tc-shared/utils/helpers";
import {FlatInputField} from "tc-shared/ui/react-elements/InputField";
import {arrayBufferBase64} from "tc-shared/utils/buffers";
import {tra} from "tc-shared/i18n/localize";
import {getIconManager} from "tc-shared/file/Icons";
const cssStyle = require("./TabHandler.scss");
@ -56,7 +57,7 @@ const GroupsListEntry = (props: { connection: ConnectionHandler, group: GroupPro
return (
<div className={cssStyle.entry + " " + (props.selected ? cssStyle.selected : "")} onClick={props.callbackSelect}
onContextMenu={props.onContextMenu}>
<LocalIconRenderer icon={props.connection.fileManager.icons.load_icon(props.group.iconId)}/>
<RemoteIconRenderer icon={getIconManager().resolveIcon(props.group.iconId, props.connection.getCurrentServerUniqueId(), props.connection.handlerId)} />
<div className={cssStyle.name}>{groupTypePrefix + props.group.name + " (" + props.group.id + ")"}</div>
</div>
)
@ -476,8 +477,9 @@ class ServerClientList extends React.Component<{ connection: ConnectionHandler,
onContextMenu={e => this.onListContextMenu(e)}>
{selectedGroup ?
<div key={"selected-group"} className={cssStyle.entry + " " + cssStyle.selectedGroup}>
<div className={cssStyle.icon}><LocalIconRenderer
icon={this.props.connection.fileManager.icons.load_icon(selectedGroup.iconId)}/></div>
<div className={cssStyle.icon}>
<RemoteIconRenderer icon={getIconManager().resolveIcon(selectedGroup.iconId, this.props.connection.getCurrentServerUniqueId(), this.props.connection.handlerId)} />
</div>
<div
className={cssStyle.name}>{groupTypePrefix + selectedGroup.name + " (" + selectedGroup.id + ")"}</div>
</div>
@ -847,7 +849,7 @@ class ChannelList extends React.Component<{ connection: ConnectionHandler, event
id: e.id
})}
>
<LocalIconRenderer icon={this.props.connection.fileManager.icons.load_icon(e.iconId)}/>
<RemoteIconRenderer icon={getIconManager().resolveIcon(e.iconId, this.props.connection.getCurrentServerUniqueId(), this.props.connection.handlerId)} />
<a className={cssStyle.name}>{e.name + " (" + e.id + ")"}</a>
</div>
))}

View File

@ -1,8 +1,9 @@
import * as React from "react";
import {LocalIcon} from "tc-shared/file/Icons";
import {RemoteIcon} from "tc-shared/file/Icons";
import {useState} from "react";
export const IconRenderer = (props: {
icon: string | LocalIcon;
icon: string;
title?: string;
className?: string;
}) => {
@ -10,59 +11,42 @@ export const IconRenderer = (props: {
return <div className={"icon-container icon-empty " + props.className} title={props.title} />;
} else if(typeof props.icon === "string") {
return <div className={"icon " + props.icon + " " + props.className} title={props.title} />;
} else if(props.icon instanceof LocalIcon) {
return <LocalIconRenderer icon={props.icon} title={props.title} className={props.className} />;
} else {
throw "JQuery icons are not longer supported";
}
}
export interface LoadedIconRenderer {
icon: LocalIcon;
title?: string;
className?: string;
}
export const RemoteIconRenderer = (props: { icon: RemoteIcon, className?: string, title?: string }) => {
const [ revision, setRevision ] = useState(0);
export class LocalIconRenderer extends React.Component<LoadedIconRenderer, {}> {
private readonly callback_state_update;
props.icon.events.reactUse("notify_state_changed", () => setRevision(revision + 1));
constructor(props) {
super(props);
switch (props.icon.getState()) {
case "empty":
case "destroyed":
return <div key={"empty"} className={"icon-container icon-empty " + props.className} title={props.title} />;
this.callback_state_update = () => {
const icon = this.props.icon;
if(icon.status !== "destroyed")
this.forceUpdate();
};
}
case "loaded":
if(props.icon.iconId >= 0 && props.icon.iconId <= 1000) {
if(props.icon.iconId === 0) {
return <div key={"loaded-empty"} className={"icon-container icon-empty " + props.className} title={props.title} />;
}
render() {
const icon = this.props.icon;
if(!icon || icon.status === "empty" || icon.status === "destroyed")
return <div key={"empty"} className={"icon-container icon-empty " + this.props.className} title={this.props.title} />;
else if(icon.status === "loaded") {
if(icon.icon_id >= 0 && icon.icon_id <= 1000) {
if(icon.icon_id === 0)
return <div key={"loaded-empty"} className={"icon-container icon-empty"} title={this.props.title} />;
return <div key={"loaded"} className={"icon_em client-group_" + icon.icon_id} />;
return <div key={"loaded"} className={"icon_em client-group_" + props.icon.iconId + " " + props.className} title={props.title} />;
}
return <div key={"icon"} className={"icon-container " + this.props.className}><img style={{ maxWidth: "100%", maxHeight: "100%" }} src={icon.loaded_url} alt={this.props.title || ("icon " + icon.icon_id)} draggable={false} /></div>;
} else if(icon.status === "loading")
return <div key={"loading"} className={"icon-container " + this.props.className} title={this.props.title}><div className={"icon_loading"} /></div>;
else if(icon.status === "error")
return <div key={"error"} className={"icon client-warning " + this.props.className} title={icon.error_message || tr("Failed to load icon")} />;
}
return (
<div key={"icon"} className={"icon-container " + props.className} x-debug-version={2}>
<img style={{ maxWidth: "100%", maxHeight: "100%" }} src={props.icon.getImageUrl()} alt={props.title || ("icon " + props.icon.iconId)} draggable={false} />
</div>
);
componentDidMount(): void {
this.props.icon?.status_change_callbacks.push(this.callback_state_update);
}
case "loading":
return <div key={"loading"} className={"icon-container " + props.className} title={props.title}><div className={"icon_loading"} /></div>;
componentWillUnmount(): void {
this.props.icon?.status_change_callbacks.remove(this.callback_state_update);
}
case "error":
return <div key={"error"} className={"icon client-warning " + props.className} title={props.icon.getErrorMessage() || tr("Failed to load icon")} />;
componentDidUpdate(prevProps: Readonly<LoadedIconRenderer>, prevState: Readonly<{}>, snapshot?: any): void {
prevProps.icon?.status_change_callbacks.remove(this.callback_state_update);
this.props.icon?.status_change_callbacks.push(this.callback_state_update);
default:
throw "invalid icon state";
}
}
};

View File

@ -5,7 +5,7 @@ import {
} from "tc-shared/ui/react-elements/ReactComponentBase";
import * as React from "react";
import {ChannelEntry as ChannelEntryController, ChannelEvents, ChannelProperties} from "../../tree/Channel";
import {LocalIconRenderer} from "tc-shared/ui/react-elements/Icon";
import {RemoteIconRenderer} from "tc-shared/ui/react-elements/Icon";
import {EventHandler, ReactEventHandler} from "tc-shared/events";
import {Settings, settings} from "tc-shared/settings";
import {TreeEntry, UnreadMarker} from "tc-shared/ui/tree/TreeEntry";
@ -14,6 +14,7 @@ import {ClientIconRenderer} from "tc-shared/ui/react-elements/Icons";
import {ClientIcon} from "svg-sprites/client-icons";
import {VoiceConnectionStatus} from "tc-shared/connection/VoiceConnection";
import {AbstractServerConnection} from "tc-shared/connection/ConnectionBase";
import {getIconManager} from "tc-shared/file/Icons";
const channelStyle = require("./Channel.scss");
const viewStyle = require("./View.scss");
@ -84,25 +85,33 @@ class ChannelEntryIcons extends ReactComponentBase<ChannelEntryIconsProperties,
if (!this.state.icons_shown)
return null;
if (this.state.is_default)
if (this.state.is_default) {
icons.push(<ClientIconRenderer key={"icon-default"} icon={ClientIcon.ChannelDefault}
title={tr("Default channel")}/>);
}
if (this.state.is_password_protected)
if (this.state.is_password_protected) {
icons.push(<ClientIconRenderer key={"icon-protected"} icon={ClientIcon.Register}
title={tr("The channel is password protected")}/>);
}
if (this.state.is_music_quality)
if (this.state.is_music_quality) {
icons.push(<ClientIconRenderer key={"icon-music"} icon={ClientIcon.Music} title={tr("Music quality")}/>);
}
if (this.state.is_moderated)
if (this.state.is_moderated) {
icons.push(<ClientIconRenderer key={"icon-moderated"} icon={ClientIcon.Moderated}
title={tr("Channel is moderated")}/>);
}
if (this.state.custom_icon_id)
icons.push(<LocalIconRenderer key={"icon-custom"}
icon={this.props.channel.channelTree.client.fileManager.icons.load_icon(this.state.custom_icon_id)}
title={tr("Client icon")}/>);
if (this.state.custom_icon_id) {
const connection = this.props.channel.channelTree.client;
icons.push(<RemoteIconRenderer
key={"icon-custom"}
title={tr("Client icon")}
icon={getIconManager().resolveIcon(this.state.custom_icon_id, connection.getCurrentServerUniqueId(), connection.handlerId)} />);
}
if (!this.state.is_codec_supported) {
icons.push(<div key={"icon-unsupported"} className={channelStyle.icon_no_sound}>
@ -112,9 +121,11 @@ class ChannelEntryIcons extends ReactComponentBase<ChannelEntryIconsProperties,
</div>);
}
return <span className={channelStyle.icons}>
{icons}
</span>
return (
<span className={channelStyle.icons}>
{icons}
</span>
);
}
@EventHandler<ChannelEvents>("notify_properties_updated")

View File

@ -7,8 +7,6 @@ import * as React from "react";
import {
ClientEntry as ClientEntryController,
ClientEvents,
ClientProperties,
ClientType,
LocalClientEntry,
MusicClientEntry
} from "../../tree/Client";
@ -16,11 +14,12 @@ import {EventHandler, ReactEventHandler} from "tc-shared/events";
import {Group, GroupEvents} from "tc-shared/permission/GroupManager";
import {Settings, settings} from "tc-shared/settings";
import {TreeEntry, UnreadMarker} from "tc-shared/ui/tree/TreeEntry";
import {LocalIconRenderer} from "tc-shared/ui/react-elements/Icon";
import {RemoteIconRenderer} from "tc-shared/ui/react-elements/Icon";
import * as DOMPurify from "dompurify";
import {ClientIcon} from "svg-sprites/client-icons";
import {ClientIconRenderer} from "tc-shared/ui/react-elements/Icons";
import {useState} from "react";
import {getIconManager} from "tc-shared/file/Icons";
const clientStyle = require("./Client.scss");
const viewStyle = require("./View.scss");
@ -73,10 +72,13 @@ class ClientServerGroupIcons extends ReactComponentBase<ClientServerGroupIconsPr
const group_icons = groups.filter(e => e?.properties.iconid)
.sort((a, b) => a.properties.sortid - b.properties.sortid);
if (group_icons.length === 0) return null;
const connection = this.props.client.channelTree.client;
return [
group_icons.map(e => {
return <LocalIconRenderer key={"group-icon-" + e.id}
icon={this.props.client.channelTree.client.fileManager.icons.load_icon(e.properties.iconid)}/>;
return <RemoteIconRenderer key={"group-icon-" + e.id}
icon={getIconManager().resolveIcon(e.properties.iconid, connection.getCurrentServerUniqueId(), connection.handlerId)} />
})
];
}
@ -126,14 +128,16 @@ class ClientChannelGroupIcon extends ReactComponentBase<ClientChannelGroupIconPr
this.subscribed_group = channel_group;
if (channel_group.properties.iconid === 0) return null;
return <LocalIconRenderer key={"cg-icon"}
icon={this.props.client.channelTree.client.fileManager.icons.load_icon(channel_group.properties.iconid)}/>;
const connection = this.props.client.channelTree.client;
return <RemoteIconRenderer icon={getIconManager().resolveIcon(channel_group.properties.iconid, connection.getCurrentServerUniqueId(), connection.handlerId)} key={"cg-icon"} />;
}
@EventHandler<ClientEvents>("notify_properties_updated")
private handlePropertiesUpdated(event: ClientEvents["notify_properties_updated"]) {
if (typeof event.updated_properties.client_servergroups)
if (typeof event.updated_properties.client_servergroups) {
this.forceUpdate();
}
}
}
@ -153,9 +157,11 @@ class ClientIcons extends ReactComponentBase<ClientIconsProperties, {}> {
icons.push(<ClientServerGroupIcons key={"sg-icons"} client={this.props.client}/>);
icons.push(<ClientChannelGroupIcon key={"channel-icons"} client={this.props.client}/>);
if (this.props.client.properties.client_icon_id !== 0)
icons.push(<LocalIconRenderer key={"client-icon"}
icon={this.props.client.channelTree.client.fileManager.icons.load_icon(this.props.client.properties.client_icon_id)}/>);
if (this.props.client.properties.client_icon_id !== 0) {
const connection = this.props.client.channelTree.client;
icons.push(<RemoteIconRenderer key={"client-icon"}
icon={getIconManager().resolveIcon(this.props.client.properties.client_icon_id, connection.getCurrentServerUniqueId(), connection.handlerId)} />);
}
return (
<div className={clientStyle.containerIcons}>

View File

@ -1,11 +1,12 @@
import {BatchUpdateAssignment, BatchUpdateType} from "tc-shared/ui/react-elements/ReactComponentBase";
import {ServerEntry as ServerEntryController, ServerEvents} from "../../tree/Server";
import * as React from "react";
import {LocalIconRenderer} from "tc-shared/ui/react-elements/Icon";
import {LocalIconRenderer, RemoteIconRenderer} from "tc-shared/ui/react-elements/Icon";
import {EventHandler, ReactEventHandler} from "tc-shared/events";
import {Settings, settings} from "tc-shared/settings";
import {TreeEntry, UnreadMarker} from "tc-shared/ui/tree/TreeEntry";
import {ConnectionEvents, ConnectionState} from "tc-shared/ConnectionHandler";
import {getIconManager} from "tc-shared/file/Icons";
const serverStyle = require("./Server.scss");
const viewStyle = require("./View.scss");
@ -72,6 +73,8 @@ export class ServerEntry extends TreeEntry<ServerEntryProperties, ServerEntrySta
else if (this.state.connection_state === "connecting")
name = tr("Connecting to ") + this.props.server.remote_address.host + (this.props.server.remote_address.port !== 9987 ? ":" + this.props.server.remote_address.host : "");
const connection = this.props.server.channelTree.client;
return <div
className={this.classList(serverStyle.serverEntry, viewStyle.treeEntry, this.props.server.isSelected() && viewStyle.selected)}
style={{top: this.props.offset}}
@ -81,8 +84,7 @@ export class ServerEntry extends TreeEntry<ServerEntryProperties, ServerEntrySta
<UnreadMarker entry={this.props.server}/>
<div className={"icon client-server_green " + serverStyle.server_type}/>
<div className={this.classList(serverStyle.name)}>{name}</div>
<LocalIconRenderer
icon={this.props.server.channelTree.client.fileManager?.icons.load_icon(this.props.server.properties.virtualserver_icon_id)}/>
<RemoteIconRenderer icon={getIconManager().resolveIcon(this.props.server.properties.virtualserver_icon_id, this.props.server.properties.virtualserver_unique_identifier, connection.handlerId)} />
</div>
}

View File

@ -23,5 +23,4 @@ export class UnreadMarker extends ReactComponentBase<UnreadMarkerProperties, {}>
}
}
export class TreeEntry<Props, State> extends ReactComponentBase<Props, State> {
}
export class TreeEntry<Props, State> extends ReactComponentBase<Props, State> { }

View File

@ -23,7 +23,6 @@ import {ConnectionEvents} from "tc-shared/ConnectionHandler";
const viewStyle = require("./View.scss");
export interface ChannelTreeViewProperties {
tree: ChannelTree;
onMoveStart: (start: { x: number, y: number }, current: { x: number, y: number }) => void;