fixed the remote icon handler
parent
ef07b0c2e5
commit
3fb5ccf8cf
|
@ -1,6 +1,6 @@
|
|||
import {Registry} from "tc-shared/events";
|
||||
|
||||
export const kIPCIconChannel = "avatars";
|
||||
export const kIPCIconChannel = "icons";
|
||||
export const kGlobalIconHandlerId = "global";
|
||||
|
||||
export interface RemoteIconEvents {
|
||||
|
@ -22,8 +22,8 @@ export abstract class RemoteIcon {
|
|||
|
||||
private state: RemoteIconState;
|
||||
|
||||
private imageUrl: string;
|
||||
private errorMessage: string;
|
||||
protected imageUrl: string;
|
||||
protected errorMessage: string;
|
||||
|
||||
protected constructor(serverUniqueId: string, iconId: number) {
|
||||
this.events = new Registry<RemoteIconEvents>();
|
||||
|
@ -55,6 +55,10 @@ export abstract class RemoteIcon {
|
|||
this.events.fire("notify_state_changed", { newState: state, oldState: oldState });
|
||||
}
|
||||
|
||||
hasImageUrl() : boolean {
|
||||
return !!this.imageUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will throw an string if the icon isn't in loaded state
|
||||
*/
|
||||
|
@ -64,7 +68,7 @@ export abstract class RemoteIcon {
|
|||
}
|
||||
|
||||
if(!this.imageUrl) {
|
||||
throw tr("remote icon is missing an image url");
|
||||
throw tra("remote {} icon is missing an image url", this.iconId);
|
||||
}
|
||||
|
||||
return this.imageUrl;
|
||||
|
@ -112,6 +116,10 @@ export abstract class RemoteIcon {
|
|||
}
|
||||
|
||||
export abstract class AbstractIconManager {
|
||||
protected static iconUniqueKey(iconId: number, serverUniqueId: string) : string {
|
||||
return "v2-" + serverUniqueId + "-" + iconId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param iconId The requested icon
|
||||
* @param serverUniqueId The server unique id for the icon
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import {ImageCache, ImageType, imageType2MediaType, responseImageType} from "tc-shared/file/ImageCache";
|
||||
import {AbstractIconManager, RemoteIcon, RemoteIconState, setIconManager} from "tc-shared/file/Icons";
|
||||
import {AbstractIconManager, kIPCIconChannel, RemoteIcon, RemoteIconState, setIconManager} from "tc-shared/file/Icons";
|
||||
import * as log from "tc-shared/log";
|
||||
import {LogCategory, logDebug, logError, logWarn} from "tc-shared/log";
|
||||
import {server_connections} from "tc-shared/ConnectionManager";
|
||||
|
@ -10,6 +10,9 @@ import {FileTransferState, ResponseTransferTarget, TransferProvider, TransferTar
|
|||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
|
||||
import {ErrorCode} from "tc-shared/connection/ErrorCode";
|
||||
import {ChannelMessage, IPCChannel} from "tc-shared/ipc/BrowserIPC";
|
||||
import * as ipc from "tc-shared/ipc/BrowserIPC";
|
||||
import {kIPCAvatarChannel} from "tc-shared/file/Avatars";
|
||||
|
||||
/* TODO: Retry icon download after some time */
|
||||
/* TODO: Download icon when we're connected to the server were we want the icon from and update the icon */
|
||||
|
@ -38,6 +41,13 @@ class LocalRemoteIcon extends RemoteIcon {
|
|||
super(serverUniqueId, iconId);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
super.destroy();
|
||||
if(this.imageUrl && "revokeObjectURL" in URL) {
|
||||
URL.revokeObjectURL(this.imageUrl);
|
||||
}
|
||||
}
|
||||
|
||||
public setImageUrl(url: string) {
|
||||
super.setImageUrl(url);
|
||||
}
|
||||
|
@ -55,14 +65,14 @@ export let localIconCache: ImageCache;
|
|||
class IconManager extends AbstractIconManager {
|
||||
private cachedIcons: {[key: string]: LocalRemoteIcon} = {};
|
||||
private connectionStateChangeListener: {[key: string]: (handlerId: string, event: ConnectionEvents["notify_connection_state_changed"]) => void} = {};
|
||||
|
||||
private static iconUniqueKey(iconId: number, serverUniqueId: string) : string {
|
||||
return "v2-" + serverUniqueId + "-" + iconId;
|
||||
}
|
||||
private ipcChannel: IPCChannel;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.ipcChannel = ipc.getInstance().createChannel(undefined, kIPCIconChannel);
|
||||
this.ipcChannel.messageHandler = this.handleIpcMessage.bind(this);
|
||||
|
||||
server_connections.events().on("notify_handler_created", event => {
|
||||
this.connectionStateChangeListener[event.handlerId] = this.handleHandlerStateChange.bind(this, event.handlerId);
|
||||
event.handler.events().on("notify_connection_state_changed", this.connectionStateChangeListener[event.handlerId] as any);
|
||||
|
@ -76,6 +86,13 @@ class IconManager extends AbstractIconManager {
|
|||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
Object.values(this.cachedIcons).forEach(icon => icon.destroy());
|
||||
this.cachedIcons = {};
|
||||
|
||||
/* TODO: Unregister server handler events */
|
||||
}
|
||||
|
||||
private handleHandlerStateChange(handlerId: string, event: ConnectionEvents["notify_connection_state_changed"]) {
|
||||
const connection = server_connections.findConnection(handlerId);
|
||||
if(!connection) {
|
||||
|
@ -99,6 +116,40 @@ class IconManager extends AbstractIconManager {
|
|||
});
|
||||
}
|
||||
|
||||
private handleIconStateChanged(icon: RemoteIcon) {
|
||||
this.sendIconStateChange(icon);
|
||||
}
|
||||
|
||||
private sendIconStateChange(icon: RemoteIcon, remoteId?: string) {
|
||||
let data = {} as any;
|
||||
|
||||
data.iconUniqueId = IconManager.iconUniqueKey(icon.iconId, icon.serverUniqueId);
|
||||
data.status = icon.getState();
|
||||
|
||||
switch (icon.getState()) {
|
||||
case "loaded":
|
||||
data.url = icon.hasImageUrl() ? icon.getImageUrl() : undefined;
|
||||
break;
|
||||
|
||||
case "error":
|
||||
data.errorMessage = icon.getErrorMessage();
|
||||
break;
|
||||
}
|
||||
|
||||
this.ipcChannel.sendMessage("notify-icon-status", data, remoteId);
|
||||
}
|
||||
|
||||
|
||||
private handleIpcMessage(remoteId: string, broadcast: boolean, message: ChannelMessage) {
|
||||
if(broadcast) { return; }
|
||||
if(message.type === "initialize") {
|
||||
this.ipcChannel.sendMessage("initialized", {}, remoteId);
|
||||
return;
|
||||
} else if(message.type === "icon-resolve") {
|
||||
this.sendIconStateChange(this.resolveIcon(message.data.iconId, message.data.serverUniqueId, message.data.handlerId), remoteId);
|
||||
}
|
||||
}
|
||||
|
||||
resolveIcon(iconId: number, serverUniqueId: string, handlerIdHint: string): RemoteIcon {
|
||||
/* just to ensure */
|
||||
iconId = iconId >>> 0;
|
||||
|
@ -111,6 +162,8 @@ class IconManager extends AbstractIconManager {
|
|||
let icon = new LocalRemoteIcon(serverUniqueId, iconId);
|
||||
this.cachedIcons[iconUniqueId] = icon;
|
||||
|
||||
icon.events.on("notify_state_changed", () => this.handleIconStateChanged(icon));
|
||||
|
||||
if(iconId >= 0 && iconId <= 1000) {
|
||||
icon.setState("loaded");
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
import {AbstractIconManager, kIPCIconChannel, RemoteIcon, RemoteIconState, setIconManager} from "tc-shared/file/Icons";
|
||||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import {ChannelMessage, IPCChannel} from "tc-shared/ipc/BrowserIPC";
|
||||
import * as ipc from "tc-shared/ipc/BrowserIPC";
|
||||
import {Settings} from "tc-shared/settings";
|
||||
import {LogCategory, logWarn} from "tc-shared/log";
|
||||
|
||||
class RemoteRemoteIcon extends RemoteIcon {
|
||||
constructor(serverUniqueId: string, iconId: number) {
|
||||
super(serverUniqueId, iconId);
|
||||
}
|
||||
|
||||
public setState(state: RemoteIconState) {
|
||||
super.setState(state);
|
||||
}
|
||||
|
||||
public setErrorMessage(message: string) {
|
||||
super.setErrorMessage(message);
|
||||
}
|
||||
|
||||
public setImageUrl(url: string) {
|
||||
super.setImageUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
class RemoteIconManager extends AbstractIconManager {
|
||||
private readonly ipcChannel: IPCChannel;
|
||||
private callbackInitialized: () => void;
|
||||
|
||||
private cachedIcons: {[key: string]: RemoteRemoteIcon} = {};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.ipcChannel = ipc.getInstance().createChannel(Settings.instance.static(Settings.KEY_IPC_REMOTE_ADDRESS, "invalid"), kIPCIconChannel);
|
||||
this.ipcChannel.messageHandler = this.handleIpcMessage.bind(this);
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
this.ipcChannel.sendMessage("initialize", {});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(() => {
|
||||
this.callbackInitialized = undefined;
|
||||
reject(tr("initialize timeout"));
|
||||
}, 5000);
|
||||
|
||||
this.callbackInitialized = () => {
|
||||
clearTimeout(timeout);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
resolveIcon(iconId: number, serverUniqueId: string, handlerId?: string): RemoteIcon {
|
||||
iconId = iconId >>> 0;
|
||||
|
||||
const uniqueId = RemoteIconManager.iconUniqueKey(iconId, serverUniqueId);
|
||||
if(this.cachedIcons[uniqueId]) { return this.cachedIcons[uniqueId]; }
|
||||
|
||||
const icon = new RemoteRemoteIcon(serverUniqueId, iconId);
|
||||
this.cachedIcons[uniqueId] = icon;
|
||||
|
||||
this.ipcChannel.sendMessage("icon-resolve", {
|
||||
iconId: iconId,
|
||||
serverUniqueId: serverUniqueId,
|
||||
handlerId: handlerId
|
||||
});
|
||||
return icon;
|
||||
}
|
||||
|
||||
|
||||
private handleIpcMessage(_remoteId: string, broadcast: boolean, message: ChannelMessage) {
|
||||
if(!broadcast) {
|
||||
if(message.type === "initialized") {
|
||||
if(this.callbackInitialized)
|
||||
this.callbackInitialized();
|
||||
}
|
||||
}
|
||||
|
||||
if(message.type === "notify-icon-status") {
|
||||
const icon = this.cachedIcons[message.data.iconUniqueId];
|
||||
if(!icon) { return; }
|
||||
|
||||
switch (message.data.status as RemoteIconState) {
|
||||
case "destroyed":
|
||||
delete this.cachedIcons[message.data.iconUniqueId];
|
||||
icon.destroy();
|
||||
break;
|
||||
|
||||
case "empty":
|
||||
icon.setState("empty");
|
||||
break;
|
||||
|
||||
case "error":
|
||||
icon.setErrorMessage(message.data.errorMessage);
|
||||
icon.setState("error");
|
||||
break;
|
||||
|
||||
case "loaded":
|
||||
icon.setImageUrl(message.data.url);
|
||||
icon.setState("loaded");
|
||||
break;
|
||||
|
||||
case "loading":
|
||||
icon.setState("loading");
|
||||
break;
|
||||
|
||||
default:
|
||||
logWarn(LogCategory.FILE_TRANSFER, tr("Received remote icon state change with an unknown state %s"), message.data.state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
|
||||
priority: 10,
|
||||
name: "IPC icon init",
|
||||
function: async () => {
|
||||
let instance = new RemoteIconManager();
|
||||
await instance.initialize();
|
||||
setIconManager(instance);
|
||||
}
|
||||
});
|
Loading…
Reference in New Issue