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.getVoiceRecorder()?.input?.setFilterMode(FilterMode.Filter);
this.update_voice_status(); this.update_voice_status();
} }
getCurrentServerUniqueId() {
return this.channelTree.server.properties.virtualserver_unique_identifier;
}
} }
export type ConnectionStateUpdateType = "microphone" | "speaker" | "away" | "subscribe" | "query"; 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 "./video-viewer/Controller";
import "./profiles/ConnectionProfile"; import "./profiles/ConnectionProfile";
import "./update/UpdaterWeb"; import "./update/UpdaterWeb";
import "./file/LocalIcons";
import {defaultConnectProfile, findConnectProfile} from "tc-shared/profiles/ConnectionProfile"; import {defaultConnectProfile, findConnectProfile} from "tc-shared/profiles/ConnectionProfile";
import {server_connections} from "tc-shared/ConnectionManager"; import {server_connections} from "tc-shared/ConnectionManager";
import {initializeConnectionUIList} from "tc-shared/ui/frames/connection-handler-list/Controller"; 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) { set collapsed(flag: boolean) {
if(this._flag_collapsed === flag) if(this._flag_collapsed === flag) {
return; return;
}
this._flag_collapsed = flag; this._flag_collapsed = flag;
this.events.fire("notify_collapsed_state_changed", { collapsed: flag }); this.events.fire("notify_collapsed_state_changed", { collapsed: flag });
this.view.current?.forceUpdate(); this.view.current?.forceUpdate();

View File

@ -20,10 +20,11 @@ import {spawnAbout} from "../../ui/modal/ModalAbout";
import * as loader from "tc-loader"; import * as loader from "tc-loader";
import {formatMessage} from "../../ui/frames/chat"; import {formatMessage} from "../../ui/frames/chat";
import {control_bar_instance} from "../../ui/frames/control-bar"; 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 {spawnPermissionEditorModal} from "../../ui/modal/permission/ModalPermissionEditor";
import {global_client_actions} from "tc-shared/events/GlobalEvents"; import {global_client_actions} from "tc-shared/events/GlobalEvents";
import {server_connections} from "tc-shared/ConnectionManager"; import {server_connections} from "tc-shared/ConnectionManager";
import {generateIconJQueryTag, getIconManager, RemoteIcon} from "tc-shared/file/Icons";
export interface HRItem { } export interface HRItem { }
@ -33,7 +34,7 @@ export interface MenuItem {
delete_item(item: MenuItem | HRItem); delete_item(item: MenuItem | HRItem);
items() : (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; label(value?: string) : string;
visible(value?: boolean) : boolean; visible(value?: boolean) : boolean;
disabled(value?: boolean) : boolean; disabled(value?: boolean) : boolean;
@ -179,12 +180,13 @@ namespace html {
return this; return this;
} }
icon(klass?: string | LocalIcon): string { icon(klass?: string | RemoteIcon): string {
this._label_icon_tag.children().remove(); 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); $.spawn("div").addClass("icon_em " + klass).appendTo(this._label_icon_tag);
else } else {
IconManager.generate_tag(klass).appendTo(this._label_icon_tag); generateIconJQueryTag(klass).appendTo(this._label_icon_tag);
}
return ""; return "";
} }
@ -290,7 +292,7 @@ export function rebuild_bookmarks() {
const bookmark = entry as Bookmark; const bookmark = entry as Bookmark;
const item = root.append_item(bookmark.display_name); 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)); 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 {MusicInfo} from "../../ui/frames/side/music_info";
import {ConversationManager} from "../../ui/frames/side/ConversationManager"; import {ConversationManager} from "../../ui/frames/side/ConversationManager";
import {PrivateConversationManager} from "../../ui/frames/side/PrivateConversationManager"; import {PrivateConversationManager} from "../../ui/frames/side/PrivateConversationManager";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
export enum InfoFrameMode { export enum InfoFrameMode {
NONE = "none", NONE = "none",
@ -126,8 +127,10 @@ export class InfoFrame {
html_tag.children().remove(); html_tag.children().remove();
if(channel) { if(channel) {
if(channel.properties.channel_icon_id != 0) if(channel.properties.channel_icon_id != 0) {
client.handle.fileManager.icons.generateTag(channel.properties.channel_icon_id).appendTo(html_tag); 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); $.spawn("div").text(channel.formattedChannelName()).appendTo(html_tag);
this.update_channel_limit(channel, html_limit_tag); this.update_channel_limit(channel, html_limit_tag);
@ -154,8 +157,10 @@ export class InfoFrame {
/* initialize */ /* initialize */
if(channel) { if(channel) {
if(channel.properties.channel_icon_id != 0) if(channel.properties.channel_icon_id != 0) {
this.handle.handle.fileManager.icons.generateTag(channel.properties.channel_icon_id).appendTo(html_tag); 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); $.spawn("div").text(channel.formattedChannelName()).appendTo(html_tag);
this.update_channel_limit(channel, html_limit_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)); html_tag.append(formatMessage(tr("Unknown channel id {}"), current_channel_id));
} else if(channel_tree && current_channel_id == 0) { } else if(channel_tree && current_channel_id == 0) {
const server = this.handle.handle.channelTree.server; const server = this.handle.handle.channelTree.server;
if(server.properties.virtualserver_icon_id != 0) if(server.properties.virtualserver_icon_id != 0) {
this.handle.handle.fileManager.icons.generateTag(server.properties.virtualserver_icon_id).appendTo(html_tag); 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); $.spawn("div").text(server.properties.virtualserver_name).appendTo(html_tag);
html_tag_title.text(tr("You're chatting in Server")); 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 {server_connections} from "tc-shared/ConnectionManager";
import {LogCategory, logWarn} from "tc-shared/log"; import {LogCategory, logWarn} from "tc-shared/log";
import {ConnectionState} from "tc-shared/ConnectionHandler"; 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() { export function initializeConnectionUIList() {
const container = document.getElementById("connection-handler-list"); const container = document.getElementById("connection-handler-list");
@ -119,19 +120,13 @@ function initializeController(events: Registry<ConnectionListUIEvents>) {
break; 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", { events.fire_async("notify_handler_status", {
handlerId: event.handlerId, handlerId: event.handlerId,
status: { status: {
handlerName: handler.channelTree.server.properties.virtualserver_name, handlerName: handler.channelTree.server.properties.virtualserver_name,
connectionState: state, connectionState: state,
voiceReplaying: handler.getServerConnection().getVoiceConnection().isReplayingVoice(), 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 MouseMoveCoordinates = { x: number, y: number, xOffset: number };
export type HandlerConnectionState = "disconnected" | "connecting" | "connected"; export type HandlerConnectionState = "disconnected" | "connecting" | "connected";
@ -7,7 +7,7 @@ export type HandlerStatus = {
connectionState: HandlerConnectionState, connectionState: HandlerConnectionState,
handlerName: string, handlerName: string,
voiceReplaying: boolean, voiceReplaying: boolean,
serverIcon: LocalIcon | undefined serverIcon: RemoteIconInfo | undefined
} }
export interface ConnectionListUIEvents { export interface ConnectionListUIEvents {

View File

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

View File

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

View File

@ -22,7 +22,6 @@ import {
import * as contextmenu from "tc-shared/ui/elements/ContextMenu"; import * as contextmenu from "tc-shared/ui/elements/ContextMenu";
import {createInputModal} from "tc-shared/ui/elements/Modal"; import {createInputModal} from "tc-shared/ui/elements/Modal";
import {global_client_actions} from "tc-shared/events/GlobalEvents"; 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"; import {ConnectionManagerEvents, server_connections} from "tc-shared/ConnectionManager";
const cssStyle = require("./index.scss"); const cssStyle = require("./index.scss");
@ -121,7 +120,7 @@ class BookmarkButton extends ReactComponentBase<{ event_registry: Registry<Inter
private renderBookmark(bookmark: Bookmark) { private renderBookmark(bookmark: Bookmark) {
return ( return (
<DropdownEntry key={bookmark.unique_id} <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} text={bookmark.display_name}
onClick={BookmarkButton.onBookmarkClick.bind(undefined, bookmark.unique_id)} onClick={BookmarkButton.onBookmarkClick.bind(undefined, bookmark.unique_id)}
onContextMenu={this.onBookmarkContextMenu.bind(this, 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 {formatDate} from "../../../MessageFormatter";
import {Settings, settings} from "../../../settings"; import {Settings, settings} from "../../../settings";
import {server_connections} from "tc-shared/ConnectionManager"; 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; 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); const connection = server_connections.findConnection(handlerId);
if(connection.channelTree.server.properties.virtualserver_icon_id) { if(connection.channelTree.server.properties.virtualserver_icon_id) {
const icon = connection.fileManager.icons.load_icon(connection.channelTree.server.properties.virtualserver_icon_id); const icon = getIconManager().resolveIcon(connection.channelTree.server.properties.virtualserver_icon_id, connection.getCurrentServerUniqueId(), connection.handlerId);
await icon.await_loading(); await icon.awaitLoaded();
return icon.loaded_url;
if(icon.getState() === "loaded" && icon.iconId > 1000) {
return icon.getImageUrl();
}
} }
return kDefaultIcon; return kDefaultIcon;

View File

@ -6,6 +6,7 @@ import * as image_preview from "../image_preview";
import * as i18nc from "../../../i18n/country"; import * as i18nc from "../../../i18n/country";
import {ClientEntry, LocalClientEntry} from "../../../tree/Client"; import {ClientEntry, LocalClientEntry} from "../../../tree/Client";
import {format_online_time} from "../../../utils/TimeUtils"; import {format_online_time} from "../../../utils/TimeUtils";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
export class ClientInfo { export class ClientInfo {
readonly handle: Frame; readonly handle: Frame;
@ -246,7 +247,7 @@ export class ClientInfo {
container_groups.append( container_groups.append(
$.spawn("div").addClass("group-container") $.spawn("div").addClass("group-container")
.append( .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( ).append(
$.spawn("a").text(group.name).attr("title", tr("Group id: ") + group.id) $.spawn("a").text(group.name).attr("title", tr("Group id: ") + group.id)
) )
@ -265,7 +266,7 @@ export class ClientInfo {
container_group.append( container_group.append(
$.spawn("div").addClass("group-container") $.spawn("div").addClass("group-container")
.append( .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( ).append(
$.spawn("a").text(group.name).attr("title", tr("Group id: ") + group_id) $.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 {formatMessage} from "../../ui/frames/chat";
import * as top_menu from "../frames/MenuBar"; import * as top_menu from "../frames/MenuBar";
import {control_bar_instance} from "../../ui/frames/control-bar"; 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() { export function spawnBookmarkModal() {
let modal: Modal; let modal: Modal;
@ -153,8 +154,8 @@ export function spawnBookmarkModal() {
if (entry.type === BookmarkType.ENTRY) { if (entry.type === BookmarkType.ENTRY) {
const bookmark = entry as Bookmark; const bookmark = entry as Bookmark;
container.append( container.append(
bookmark.last_icon_id ? bookmark.last_icon_id && bookmark.last_icon_server_id ?
IconManager.generate_tag(icon_cache_loader.load_icon(bookmark.last_icon_id, bookmark.last_icon_server_id), {animate: false}) : generateIconJQueryTag(getIconManager().resolveIcon(bookmark.last_icon_id, bookmark.last_icon_server_id), {animate: false}) :
$.spawn("div").addClass("icon-container icon_em") $.spawn("div").addClass("icon-container icon_em")
); );
} else { } else {

View File

@ -6,6 +6,7 @@ import * as i18nc from "../../i18n/country";
import * as tooltip from "../../ui/elements/Tooltip"; import * as tooltip from "../../ui/elements/Tooltip";
import * as moment from "moment"; import * as moment from "moment";
import {format_number, network} from "../../ui/frames/chat"; import {format_number, network} from "../../ui/frames/chat";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
type InfoUpdateCallback = (info: ClientConnectionInfo) => any; 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! if (!group) continue; //This shall never happen!
container_empty.hide(); container_empty.hide();
container_entries.append($.spawn("div").addClass("entry").append( 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("a").addClass("name").text(group.name + " (" + group.id + ")"),
$.spawn("div").addClass("button-delete").append( $.spawn("div").addClass("button-delete").append(
$.spawn("div").addClass("icon_em client-delete").attr("title", tr("Delete group")).on('click', event => { $.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 {KeyCode} from "../../PPTListener";
import * as i18nc from "../../i18n/country"; import * as i18nc from "../../i18n/country";
import {spawnSettingsModal} from "../../ui/modal/ModalSettings"; 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 {server_connections} from "tc-shared/ConnectionManager";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
//FIXME: Move this shit out of this file! //FIXME: Move this shit out of this file!
export namespace connection_log { export namespace connection_log {
@ -290,6 +291,7 @@ export function spawnConnectModal(options: {
/* connect history show */ /* connect history show */
{ {
for (const entry of connection_log.history().slice(0, 10)) { for (const entry of connection_log.history().slice(0, 10)) {
$.spawn("div").addClass("row").append( $.spawn("div").addClass("row").append(
$.spawn("div").addClass("column delete").append($.spawn("div").addClass("icon_em client-delete")).on('click', event => { $.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( ).append(
$.spawn("div").addClass("column name").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) $.spawn("a").text(entry.name)
]) ])
).append( ).append(

View File

@ -10,6 +10,7 @@ import * as tooltip from "../../ui/elements/Tooltip";
import {spawnIconSelect} from "../../ui/modal/ModalIconSelect"; import {spawnIconSelect} from "../../ui/modal/ModalIconSelect";
import {hashPassword} from "../../utils/helpers"; 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";
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) {
let properties: ChannelProperties = { } as ChannelProperties; //The changes properties 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_maxfamilyclients_unlimited: true,
channel_flag_maxclients_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; render_properties["create"] = !channel;
let template = $("#tmpl_channel_edit").renderTag(render_properties); let template = $("#tmpl_channel_edit").renderTag(render_properties);
@ -134,7 +136,7 @@ function applyGeneralListener(connection: ConnectionHandler, properties: Channel
spawnIconSelect(connection, id => { spawnIconSelect(connection, id => {
const icon_node = tag.find(".icon-preview"); const icon_node = tag.find(".icon-preview");
icon_node.children().remove(); 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); console.log("Selected icon ID: %d", id);
properties.channel_icon_id = id; properties.channel_icon_id = id;
@ -144,7 +146,7 @@ function applyGeneralListener(connection: ConnectionHandler, properties: Channel
tag.find(".button-icon-remove").on('click', event => { tag.find(".button-icon-remove").on('click', event => {
const icon_node = tag.find(".icon-preview"); const icon_node = tag.find(".icon-preview");
icon_node.children().remove(); 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"); console.log("Remove channel icon");
properties.channel_icon_id = 0; properties.channel_icon_id = 0;

View File

@ -4,6 +4,7 @@ import * as log from "../../log";
import {ClientEntry} from "../../tree/Client"; import {ClientEntry} from "../../tree/Client";
import {GroupManager, GroupType} from "../../permission/GroupManager"; import {GroupManager, GroupType} from "../../permission/GroupManager";
import PermissionType from "../../permission/PermissionType"; import PermissionType from "../../permission/PermissionType";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
let current_modal: Modal; let current_modal: Modal;
export function createServerGroupAssignmentModal(client: ClientEntry, callback: (groups: number[], flag: boolean) => Promise<boolean>) { 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["name"] = group.name;
entry["disabled"] = !client.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_ADD_POWER).granted(group.requiredMemberRemovePower); 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; 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); groups.push(entry);
} }

View File

@ -10,6 +10,7 @@ import * as crc32 from "../../crypto/crc32";
import {FileInfo} from "../../file/FileManager"; import {FileInfo} from "../../file/FileManager";
import {FileTransferState, TransferProvider} from "../../file/Transfer"; import {FileTransferState, TransferProvider} from "../../file/Transfer";
import {ErrorCode} from "../../connection/ErrorCode"; 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) { export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id: number) => any, selected_icon?: number) {
selected_icon = selected_icon || 0; selected_icon = selected_icon || 0;
@ -49,22 +50,24 @@ export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id:
const update_local_icons = (icons: number[]) => { const update_local_icons = (icons: number[]) => {
container_icons_local.empty(); container_icons_local.empty();
for (const icon_id of icons) { for (const iconId of icons) {
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) { if (callback_icon) {
tag.on('click', event => { tag.on('click', event => {
container_icons.find(".selected").removeClass("selected"); container_icons.find(".selected").removeClass("selected");
tag.addClass("selected"); tag.addClass("selected");
selected_container.empty().append(tag.clone()); selected_container.empty().append(tag.clone());
selected_icon = icon_id; selected_icon = iconId;
button_select.prop("disabled", false); button_select.prop("disabled", false);
}); });
tag.on('dblclick', event => { tag.on('dblclick', event => {
callback_icon(icon_id); callback_icon(iconId);
modal.close(); modal.close();
}); });
if (icon_id == selected_icon) if (iconId == selected_icon)
tag.trigger('click'); tag.trigger('click');
} }
tag.appendTo(container_icons_local); tag.appendTo(container_icons_local);
@ -101,19 +104,21 @@ export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id:
if (!chunk) return; if (!chunk) return;
for (const icon of chunk) { for (const icon of chunk) {
const icon_id = parseInt(icon.name.substr("icon_".length)); const iconId = parseInt(icon.name.substr("icon_".length));
if (Number.isNaN(icon_id)) { if (Number.isNaN(iconId)) {
log.warn(LogCategory.GENERAL, tr("Received an unparsable icon within icon list (%o)"), icon); log.warn(LogCategory.GENERAL, tr("Received an unparsable icon within icon list (%o)"), icon);
continue; 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) { if (callback_icon || allow_manage) {
tag.on('click', event => { tag.on('click', event => {
container_icons.find(".selected").removeClass("selected"); container_icons.find(".selected").removeClass("selected");
tag.addClass("selected"); tag.addClass("selected");
selected_container.empty().append(tag.clone()); selected_container.empty().append(tag.clone());
selected_icon = icon_id; selected_icon = iconId;
button_select.prop("disabled", false); button_select.prop("disabled", false);
button_delete.prop("disabled", !allow_manage); button_delete.prop("disabled", !allow_manage);
}); });
@ -121,10 +126,10 @@ export function spawnIconSelect(client: ConnectionHandler, callback_icon?: (id:
if (!callback_icon) if (!callback_icon)
return; return;
callback_icon(icon_id); callback_icon(iconId);
modal.close(); modal.close();
}); });
if (icon_id == selected_icon) if (iconId == selected_icon)
tag.trigger('click'); tag.trigger('click');
} }
tag.appendTo(container_icons_remote); 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 */ 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(); selected.detach();
}).catch(error => { }).catch(error => {
if (error instanceof CommandResult && error.id == ErrorCode.SERVER_INSUFFICIENT_PERMISSIONS) 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 * as tooltip from "../../ui/elements/Tooltip";
import {spawnIconSelect} from "../../ui/modal/ModalIconSelect"; import {spawnIconSelect} from "../../ui/modal/ModalIconSelect";
import {network} from "../../ui/frames/chat"; import {network} from "../../ui/frames/chat";
import {generateIconJQueryTag, getIconManager} from "tc-shared/file/Icons";
export function createServerModal(server: ServerEntry, callback: (properties?: ServerProperties) => Promise<void>) { export function createServerModal(server: ServerEntry, callback: (properties?: ServerProperties) => Promise<void>) {
const properties = Object.assign({}, server.properties); const properties = Object.assign({}, server.properties);
@ -43,7 +44,7 @@ export function createServerModal(server: ServerEntry, callback: (properties?: S
header: tr("Manage the Virtual Server"), header: tr("Manage the Virtual Server"),
body: () => { body: () => {
const template = $("#tmpl_server_edit").renderTag(Object.assign(Object.assign({}, server.properties), { 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 */ /* the tab functionality */
@ -120,7 +121,7 @@ function apply_general_listener(tag: JQuery, server: ServerEntry, properties: Se
spawnIconSelect(server.channelTree.client, id => { spawnIconSelect(server.channelTree.client, id => {
const icon_node = tag.find(".icon-preview"); const icon_node = tag.find(".icon-preview");
icon_node.children().remove(); 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); console.log("Selected icon ID: %d", id);
properties.virtualserver_icon_id = 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 => { tag.find(".button-icon-remove").on('click', event => {
const icon_node = tag.find(".icon-preview"); const icon_node = tag.find(".icon-preview");
icon_node.children().remove(); 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"); console.log("Remove server icon");
properties.virtualserver_icon_id = 0; properties.virtualserver_icon_id = 0;

View File

@ -11,11 +11,12 @@ import {LogCategory} from "tc-shared/log";
import ResizeObserver from "resize-observer-polyfill"; import ResizeObserver from "resize-observer-polyfill";
import {LoadingDots} from "tc-shared/ui/react-elements/LoadingDots"; import {LoadingDots} from "tc-shared/ui/react-elements/LoadingDots";
import {Button} from "tc-shared/ui/react-elements/Button"; 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 {ConnectionHandler} from "tc-shared/ConnectionHandler";
import * as contextmenu from "tc-shared/ui/elements/ContextMenu"; import * as contextmenu from "tc-shared/ui/elements/ContextMenu";
import {copy_to_clipboard} from "tc-shared/utils/helpers"; import {copy_to_clipboard} from "tc-shared/utils/helpers";
import {createInfoModal} from "tc-shared/ui/elements/Modal"; import {createInfoModal} from "tc-shared/ui/elements/Modal";
import {getIconManager} from "tc-shared/file/Icons";
const cssStyle = require("./PermissionEditor.scss"); const cssStyle = require("./PermissionEditor.scss");
@ -165,8 +166,9 @@ const ButtonIconPreview = (props: { events: Registry<PermissionEditorEvents>, co
}); });
let icon; let icon;
if (!unset && iconId > 0) if (!unset && iconId > 0) {
icon = <LocalIconRenderer key={"icon-" + iconId} icon={props.connection.fileManager.icons.load_icon(iconId)}/>; icon = <RemoteIconRenderer key={"icon-" + iconId} icon={getIconManager().resolveIcon(iconId, props.connection.getCurrentServerUniqueId(), props.connection.handlerId)} />;
}
return ( return (
<div className={cssStyle.containerIconSelect}> <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 {ChannelInfo, GroupProperties, PermissionModalEvents} from "tc-shared/ui/modal/permission/ModalPermissionEditor";
import {PermissionEditorEvents} from "tc-shared/ui/modal/permission/PermissionEditor"; import {PermissionEditorEvents} from "tc-shared/ui/modal/permission/PermissionEditor";
import {ConnectionHandler} from "tc-shared/ConnectionHandler"; 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 {createInputModal} from "tc-shared/ui/elements/Modal";
import {Translatable} from "tc-shared/ui/react-elements/i18n"; import {Translatable} from "tc-shared/ui/react-elements/i18n";
import {LoadingDots} from "tc-shared/ui/react-elements/LoadingDots"; 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 {FlatInputField} from "tc-shared/ui/react-elements/InputField";
import {arrayBufferBase64} from "tc-shared/utils/buffers"; import {arrayBufferBase64} from "tc-shared/utils/buffers";
import {tra} from "tc-shared/i18n/localize"; import {tra} from "tc-shared/i18n/localize";
import {getIconManager} from "tc-shared/file/Icons";
const cssStyle = require("./TabHandler.scss"); const cssStyle = require("./TabHandler.scss");
@ -56,7 +57,7 @@ const GroupsListEntry = (props: { connection: ConnectionHandler, group: GroupPro
return ( return (
<div className={cssStyle.entry + " " + (props.selected ? cssStyle.selected : "")} onClick={props.callbackSelect} <div className={cssStyle.entry + " " + (props.selected ? cssStyle.selected : "")} onClick={props.callbackSelect}
onContextMenu={props.onContextMenu}> 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 className={cssStyle.name}>{groupTypePrefix + props.group.name + " (" + props.group.id + ")"}</div>
</div> </div>
) )
@ -476,8 +477,9 @@ class ServerClientList extends React.Component<{ connection: ConnectionHandler,
onContextMenu={e => this.onListContextMenu(e)}> onContextMenu={e => this.onListContextMenu(e)}>
{selectedGroup ? {selectedGroup ?
<div key={"selected-group"} className={cssStyle.entry + " " + cssStyle.selectedGroup}> <div key={"selected-group"} className={cssStyle.entry + " " + cssStyle.selectedGroup}>
<div className={cssStyle.icon}><LocalIconRenderer <div className={cssStyle.icon}>
icon={this.props.connection.fileManager.icons.load_icon(selectedGroup.iconId)}/></div> <RemoteIconRenderer icon={getIconManager().resolveIcon(selectedGroup.iconId, this.props.connection.getCurrentServerUniqueId(), this.props.connection.handlerId)} />
</div>
<div <div
className={cssStyle.name}>{groupTypePrefix + selectedGroup.name + " (" + selectedGroup.id + ")"}</div> className={cssStyle.name}>{groupTypePrefix + selectedGroup.name + " (" + selectedGroup.id + ")"}</div>
</div> </div>
@ -847,7 +849,7 @@ class ChannelList extends React.Component<{ connection: ConnectionHandler, event
id: e.id 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> <a className={cssStyle.name}>{e.name + " (" + e.id + ")"}</a>
</div> </div>
))} ))}

View File

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

View File

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

View File

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

View File

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