Allowing to drag client tags
parent
b665d69a9f
commit
a3b9b1b11e
|
@ -5,6 +5,7 @@
|
||||||
- Improved channel conversation mode detection support
|
- Improved channel conversation mode detection support
|
||||||
- Added support for HTML encoded links (Example would be when copying from Edge the URL)
|
- Added support for HTML encoded links (Example would be when copying from Edge the URL)
|
||||||
- Enabled context menus for all clickable client tags
|
- Enabled context menus for all clickable client tags
|
||||||
|
- Allowing to drag client tags
|
||||||
|
|
||||||
* **08.12.20**
|
* **08.12.20**
|
||||||
- Fixed the permission editor not resolving unique ids
|
- Fixed the permission editor not resolving unique ids
|
||||||
|
|
|
@ -215,17 +215,19 @@ export abstract class AbstractChat<Events extends AbstractChatEvents> {
|
||||||
return this.conversationMode === ChannelConversationMode.Private;
|
return this.conversationMode === ChannelConversationMode.Private;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected setConversationMode(mode: ChannelConversationMode) {
|
protected setConversationMode(mode: ChannelConversationMode, logChange: boolean) {
|
||||||
if(this.conversationMode === mode) {
|
if(this.conversationMode === mode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.registerChatEvent({
|
if(logChange) {
|
||||||
type: "mode-changed",
|
this.registerChatEvent({
|
||||||
uniqueId: guid() + "-mode-change",
|
type: "mode-changed",
|
||||||
timestamp: Date.now(),
|
uniqueId: guid() + "-mode-change",
|
||||||
newMode: mode === ChannelConversationMode.Public ? "normal" : mode === ChannelConversationMode.Private ? "private" : "none"
|
timestamp: Date.now(),
|
||||||
}, true);
|
newMode: mode === ChannelConversationMode.Public ? "normal" : mode === ChannelConversationMode.Private ? "private" : "none"
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
this.conversationMode = mode;
|
this.conversationMode = mode;
|
||||||
this.events.fire("notify_conversation_mode_changed", { newMode: mode });
|
this.events.fire("notify_conversation_mode_changed", { newMode: mode });
|
||||||
|
|
|
@ -165,18 +165,18 @@ export class ChannelConversation extends AbstractChat<ChannelConversationEvents>
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "private":
|
case "private":
|
||||||
this.setConversationMode(ChannelConversationMode.Private);
|
this.setConversationMode(ChannelConversationMode.Private, true);
|
||||||
this.setCurrentMode("normal");
|
this.setCurrentMode("normal");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "success":
|
case "success":
|
||||||
this.setConversationMode(ChannelConversationMode.Public);
|
this.setConversationMode(ChannelConversationMode.Public, true);
|
||||||
this.setCurrentMode("normal");
|
this.setCurrentMode("normal");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "unsupported":
|
case "unsupported":
|
||||||
this.crossChannelChatSupported = false;
|
this.crossChannelChatSupported = false;
|
||||||
this.setConversationMode(ChannelConversationMode.Private);
|
this.setConversationMode(ChannelConversationMode.Private, true);
|
||||||
this.setCurrentMode("normal");
|
this.setCurrentMode("normal");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -295,8 +295,8 @@ export class ChannelConversation extends AbstractChat<ChannelConversationEvents>
|
||||||
this.handle.connection.settings.changeServer(Settings.FN_CHANNEL_CHAT_READ(this.conversationId), timestamp);
|
this.handle.connection.settings.changeServer(Settings.FN_CHANNEL_CHAT_READ(this.conversationId), timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConversationMode(mode: ChannelConversationMode) {
|
public setConversationMode(mode: ChannelConversationMode, logChange: boolean) {
|
||||||
super.setConversationMode(mode);
|
super.setConversationMode(mode, logChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
public localClientSwitchedChannel(type: "join" | "leave") {
|
public localClientSwitchedChannel(type: "join" | "leave") {
|
||||||
|
@ -356,7 +356,7 @@ export class ChannelConversationManager extends AbstractChatManager<ChannelConve
|
||||||
}
|
}
|
||||||
|
|
||||||
if("channel_conversation_mode" in event.updatedProperties) {
|
if("channel_conversation_mode" in event.updatedProperties) {
|
||||||
conversation.setConversationMode(event.channel.properties.channel_conversation_mode);
|
conversation.setConversationMode(event.channel.properties.channel_conversation_mode, true);
|
||||||
conversation.updateAccessState();
|
conversation.updateAccessState();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -405,6 +405,10 @@ export class ChannelConversationManager extends AbstractChatManager<ChannelConve
|
||||||
let conversation = this.findConversation(channelId);
|
let conversation = this.findConversation(channelId);
|
||||||
if(!conversation) {
|
if(!conversation) {
|
||||||
conversation = new ChannelConversation(this, channelId);
|
conversation = new ChannelConversation(this, channelId);
|
||||||
|
const channel = this.connection.channelTree.findChannel(channelId);
|
||||||
|
if(channel) {
|
||||||
|
conversation.setConversationMode(channel.properties.channel_conversation_mode, false);
|
||||||
|
}
|
||||||
this.registerConversation(conversation);
|
this.registerConversation(conversation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -781,7 +781,26 @@ export function initializeChannelTreeController(events: Registry<ChannelTreeUIEv
|
||||||
}
|
}
|
||||||
|
|
||||||
const clients = event.entries.map(entryId => {
|
const clients = event.entries.map(entryId => {
|
||||||
const entry = channelTree.findEntryId(entryId);
|
if(entryId.type !== "client") { return; }
|
||||||
|
|
||||||
|
let entry: ServerEntry | ChannelEntry | ClientEntry;
|
||||||
|
if("uniqueTreeId" in entryId) {
|
||||||
|
entry = channelTree.findEntryId(entryId.uniqueTreeId);
|
||||||
|
} else {
|
||||||
|
let clients = channelTree.clients.filter(client => client.properties.client_unique_identifier === entryId.clientUniqueId);
|
||||||
|
if(entryId.clientId) {
|
||||||
|
clients = clients.filter(client => client.clientId() === entryId.clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entryId.clientDatabaseId) {
|
||||||
|
clients = clients.filter(client => client.properties.client_database_id === entryId.clientDatabaseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(clients.length === 1) {
|
||||||
|
entry = clients[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(!entry || !(entry instanceof ClientEntry)) {
|
if(!entry || !(entry instanceof ClientEntry)) {
|
||||||
logWarn(LogCategory.CHANNEL, tr("Received client move notify with an entry id which isn't a client. Entry id: %o"), entryId);
|
logWarn(LogCategory.CHANNEL, tr("Received client move notify with an entry id which isn't a client. Entry id: %o"), entryId);
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -808,7 +827,15 @@ export function initializeChannelTreeController(events: Registry<ChannelTreeUIEv
|
||||||
}
|
}
|
||||||
|
|
||||||
let channels = event.entries.map(entryId => {
|
let channels = event.entries.map(entryId => {
|
||||||
const entry = channelTree.findEntryId(entryId);
|
if(entryId.type !== "channel") { return; }
|
||||||
|
|
||||||
|
let entry: ServerEntry | ChannelEntry | ClientEntry;
|
||||||
|
if("uniqueTreeId" in entryId) {
|
||||||
|
entry = channelTree.findEntryId(entryId.uniqueTreeId);
|
||||||
|
} else {
|
||||||
|
entry = channelTree.findChannel(entryId.channelId);
|
||||||
|
}
|
||||||
|
|
||||||
if(!entry || !(entry instanceof ChannelEntry)) {
|
if(!entry || !(entry instanceof ChannelEntry)) {
|
||||||
logWarn(LogCategory.CHANNEL, tr("Received channel move notify with a channel id which isn't a channel. Entry id: %o"), entryId);
|
logWarn(LogCategory.CHANNEL, tr("Received channel move notify with a channel id which isn't a channel. Entry id: %o"), entryId);
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
|
@ -37,8 +37,8 @@ export interface ChannelTreeUIEvents {
|
||||||
action_channel_open_file_browser: { treeEntryId: number },
|
action_channel_open_file_browser: { treeEntryId: number },
|
||||||
action_client_double_click: { treeEntryId: number },
|
action_client_double_click: { treeEntryId: number },
|
||||||
action_client_name_submit: { treeEntryId: number, name: string },
|
action_client_name_submit: { treeEntryId: number, name: string },
|
||||||
action_move_clients: { targetTreeEntry: number, entries: number[] },
|
action_move_clients: { targetTreeEntry: number, entries: ChannelTreeDragEntry[] },
|
||||||
action_move_channels: { targetTreeEntry: number, mode: "before" | "after" | "child", entries: number[] },
|
action_move_channels: { targetTreeEntry: number, mode: "before" | "after" | "child", entries: ChannelTreeDragEntry[] },
|
||||||
|
|
||||||
/* queries */
|
/* queries */
|
||||||
query_tree_entries: {},
|
query_tree_entries: {},
|
||||||
|
@ -82,11 +82,30 @@ export interface ChannelTreeUIEvents {
|
||||||
notify_destroy: {}
|
notify_destroy: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ChannelTreeDragEntry = {
|
||||||
|
type: "channel",
|
||||||
|
uniqueTreeId: number,
|
||||||
|
} | {
|
||||||
|
type: "channel",
|
||||||
|
channelId: number
|
||||||
|
} | {
|
||||||
|
type: "server"
|
||||||
|
} | {
|
||||||
|
type: "client",
|
||||||
|
|
||||||
|
uniqueTreeId: number,
|
||||||
|
} | {
|
||||||
|
type: "client",
|
||||||
|
|
||||||
|
clientUniqueId: string,
|
||||||
|
clientId?: number,
|
||||||
|
clientDatabaseId?: number
|
||||||
|
};
|
||||||
|
|
||||||
export type ChannelTreeDragData = {
|
export type ChannelTreeDragData = {
|
||||||
version: 1,
|
version: 1,
|
||||||
handlerId: string,
|
handlerId: string,
|
||||||
type: string,
|
type: string,
|
||||||
|
|
||||||
entryIds: number[],
|
entries: ChannelTreeDragEntry[],
|
||||||
entryTypes: ("server" | "channel" | "client")[]
|
|
||||||
};
|
};
|
|
@ -1,4 +1,3 @@
|
||||||
import {RDPChannel, RDPChannelTree, RDPClient, RDPEntry, RDPServer} from "./RendererDataProvider";
|
|
||||||
import * as loader from "tc-loader";
|
import * as loader from "tc-loader";
|
||||||
import {Stage} from "tc-loader";
|
import {Stage} from "tc-loader";
|
||||||
import {
|
import {
|
||||||
|
@ -9,7 +8,7 @@ import {
|
||||||
spriteWidth as kClientSpriteWidth,
|
spriteWidth as kClientSpriteWidth,
|
||||||
} from "svg-sprites/client-icons";
|
} from "svg-sprites/client-icons";
|
||||||
import {LogCategory, logDebug} from "tc-shared/log";
|
import {LogCategory, logDebug} from "tc-shared/log";
|
||||||
import {ChannelTreeDragData} from "tc-shared/ui/tree/Definitions";
|
import {ChannelTreeDragData, ChannelTreeDragEntry} from "tc-shared/ui/tree/Definitions";
|
||||||
|
|
||||||
let spriteImage: HTMLImageElement;
|
let spriteImage: HTMLImageElement;
|
||||||
|
|
||||||
|
@ -29,7 +28,12 @@ function paintClientIcon(context: CanvasRenderingContext2D, icon: ClientIcon, of
|
||||||
context.drawImage(spriteImage, sprite.xOffset, sprite.yOffset, sprite.width, sprite.height, offsetX, offsetY, width, height);
|
context.drawImage(spriteImage, sprite.xOffset, sprite.yOffset, sprite.width, sprite.height, offsetX, offsetY, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateDragElement(entries: RDPEntry[]) : HTMLElement {
|
export type DragImageEntryType = {
|
||||||
|
icon: ClientIcon,
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateDragElement(entries: DragImageEntryType[]) : HTMLElement {
|
||||||
const totalHeight = entries.length * 18 + 2; /* the two extra for "low" letters like "gyj" etc. */
|
const totalHeight = entries.length * 18 + 2; /* the two extra for "low" letters like "gyj" etc. */
|
||||||
const totalWidth = 250;
|
const totalWidth = 250;
|
||||||
|
|
||||||
|
@ -37,44 +41,19 @@ export function generateDragElement(entries: RDPEntry[]) : HTMLElement {
|
||||||
let offsetX = 20;
|
let offsetX = 20;
|
||||||
|
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
|
document.body.appendChild(canvas);
|
||||||
canvas.height = totalHeight;
|
canvas.height = totalHeight;
|
||||||
canvas.width = totalWidth;
|
canvas.width = totalWidth;
|
||||||
|
|
||||||
/* TODO: With font size? */
|
/* TODO: With font size? */
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
{
|
{
|
||||||
const ctx = canvas.getContext("2d");
|
|
||||||
ctx.textAlign = "left";
|
ctx.textAlign = "left";
|
||||||
ctx.textBaseline = "bottom";
|
ctx.textBaseline = "bottom";
|
||||||
ctx.font = "700 16px Roboto, Helvetica, Arial, sans-serif";
|
ctx.font = "700 16px Roboto, Helvetica, Arial, sans-serif";
|
||||||
|
|
||||||
for(const entry of entries) {
|
for(const entry of entries) {
|
||||||
let name: string;
|
|
||||||
let icon: ClientIcon;
|
|
||||||
|
|
||||||
if(entry instanceof RDPClient) {
|
|
||||||
name = entry.name.name;
|
|
||||||
icon = entry.status;
|
|
||||||
} else if(entry instanceof RDPChannel) {
|
|
||||||
name = entry.info?.name;
|
|
||||||
icon = entry.icon;
|
|
||||||
} else if(entry instanceof RDPServer) {
|
|
||||||
icon = ClientIcon.ServerGreen;
|
|
||||||
|
|
||||||
switch (entry.state.state) {
|
|
||||||
case "connected":
|
|
||||||
name = entry.state.name;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "disconnected":
|
|
||||||
name = tr("Not connected");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "connecting":
|
|
||||||
name = tr("Connecting");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ctx.strokeStyle = "red";
|
ctx.strokeStyle = "red";
|
||||||
ctx.moveTo(offsetX, offsetY);
|
ctx.moveTo(offsetX, offsetY);
|
||||||
|
@ -83,8 +62,8 @@ export function generateDragElement(entries: RDPEntry[]) : HTMLElement {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ctx.fillStyle = "black";
|
ctx.fillStyle = "black";
|
||||||
paintClientIcon(ctx, icon, offsetX + 1, offsetY + 1, 16, 16);
|
paintClientIcon(ctx, entry.icon, offsetX + 1, offsetY + 1, 16, 16);
|
||||||
ctx.fillText(name, offsetX + 20, offsetY + 19);
|
ctx.fillText(entry.name, offsetX + 20, offsetY + 19);
|
||||||
|
|
||||||
offsetY += 18;
|
offsetY += 18;
|
||||||
|
|
||||||
|
@ -98,9 +77,9 @@ export function generateDragElement(entries: RDPEntry[]) : HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.style.position = "absolute";
|
canvas.style.position = "absolute";
|
||||||
canvas.style.left = "-100000000px";
|
canvas.style.zIndex = "100000";
|
||||||
canvas.style.top = (Math.random() * 1000000).toFixed(0) + "px";
|
canvas.style.top = "0";
|
||||||
document.body.appendChild(canvas);
|
canvas.style.left = -canvas.width + "px";
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
canvas.remove();
|
canvas.remove();
|
||||||
|
@ -113,43 +92,19 @@ const kDragDataType = "application/x-teaspeak-channel-move";
|
||||||
const kDragHandlerPrefix = "application/x-teaspeak-handler-";
|
const kDragHandlerPrefix = "application/x-teaspeak-handler-";
|
||||||
const kDragTypePrefix = "application/x-teaspeak-type-";
|
const kDragTypePrefix = "application/x-teaspeak-type-";
|
||||||
|
|
||||||
export function setupDragData(transfer: DataTransfer, tree: RDPChannelTree, entries: RDPEntry[], type: string) {
|
export function setupDragData(transfer: DataTransfer, handlerId: string, entries: ChannelTreeDragEntry[], type: string) {
|
||||||
let data: ChannelTreeDragData = {
|
let data: ChannelTreeDragData = {
|
||||||
version: 1,
|
version: 1,
|
||||||
handlerId: tree.handlerId,
|
handlerId: handlerId,
|
||||||
entryIds: entries.map(e => e.entryId),
|
entries: entries,
|
||||||
entryTypes: entries.map(entry => {
|
|
||||||
if(entry instanceof RDPServer) {
|
|
||||||
return "server";
|
|
||||||
} else if(entry instanceof RDPClient) {
|
|
||||||
return "client";
|
|
||||||
} else {
|
|
||||||
return "channel";
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
type: type
|
type: type
|
||||||
};
|
};
|
||||||
|
|
||||||
transfer.effectAllowed = "all"
|
transfer.effectAllowed = "all"
|
||||||
transfer.dropEffect = "move";
|
transfer.dropEffect = "move";
|
||||||
transfer.setData(kDragHandlerPrefix + tree.handlerId, "");
|
transfer.setData(kDragHandlerPrefix + handlerId, "");
|
||||||
transfer.setData(kDragTypePrefix + type, "");
|
transfer.setData(kDragTypePrefix + type, "");
|
||||||
transfer.setData(kDragDataType, JSON.stringify(data));
|
transfer.setData(kDragDataType, JSON.stringify(data));
|
||||||
|
|
||||||
{
|
|
||||||
let texts = [];
|
|
||||||
for(const entry of entries) {
|
|
||||||
if(entry instanceof RDPClient) {
|
|
||||||
texts.push(entry.name?.name);
|
|
||||||
} else if(entry instanceof RDPChannel) {
|
|
||||||
texts.push(entry.info?.name);
|
|
||||||
} else if(entry instanceof RDPServer) {
|
|
||||||
texts.push(entry.state.state === "connected" ? entry.state.name : undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
transfer.setData("text/plain", texts.filter(e => !!e).join(", "));
|
|
||||||
}
|
|
||||||
/* TODO: Other things as well! */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseDragData(transfer: DataTransfer) : ChannelTreeDragData | undefined {
|
export function parseDragData(transfer: DataTransfer) : ChannelTreeDragData | undefined {
|
||||||
|
|
|
@ -3,6 +3,8 @@ import * as loader from "tc-loader";
|
||||||
import {Stage} from "tc-loader";
|
import {Stage} from "tc-loader";
|
||||||
import {getIpcInstance, IPCChannel} from "tc-shared/ipc/BrowserIPC";
|
import {getIpcInstance, IPCChannel} from "tc-shared/ipc/BrowserIPC";
|
||||||
import {Settings} from "tc-shared/settings";
|
import {Settings} from "tc-shared/settings";
|
||||||
|
import {generateDragElement, setupDragData} from "tc-shared/ui/tree/DragHelper";
|
||||||
|
import {ClientIcon} from "svg-sprites/client-icons";
|
||||||
|
|
||||||
const kIpcChannel = "entry-tags";
|
const kIpcChannel = "entry-tags";
|
||||||
const cssStyle = require("./EntryTags.scss");
|
const cssStyle = require("./EntryTags.scss");
|
||||||
|
@ -24,6 +26,22 @@ export const ClientTag = (props: { clientName: string, clientUniqueId: string, h
|
||||||
pageY: event.pageY
|
pageY: event.pageY
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
draggable={true}
|
||||||
|
onDragStart={event => {
|
||||||
|
/* clients only => move */
|
||||||
|
event.dataTransfer.effectAllowed = "move"; /* prohibit copying */
|
||||||
|
event.dataTransfer.dropEffect = "move";
|
||||||
|
event.dataTransfer.setDragImage(generateDragElement([{ icon: ClientIcon.PlayerOn, name: props.clientName }]), 0, 6);
|
||||||
|
setupDragData(event.dataTransfer, props.handlerId, [
|
||||||
|
{
|
||||||
|
type: "client",
|
||||||
|
clientUniqueId: props.clientUniqueId,
|
||||||
|
clientId: props.clientId,
|
||||||
|
clientDatabaseId: props.clientDatabaseId
|
||||||
|
}
|
||||||
|
], "client");
|
||||||
|
event.dataTransfer.setData("text/plain", props.clientName);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{props.clientName}
|
{props.clientName}
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,6 +61,19 @@ export const ChannelTag = (props: { channelName: string, channelId: number, hand
|
||||||
pageY: event.pageY
|
pageY: event.pageY
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
draggable={true}
|
||||||
|
onDragStart={event => {
|
||||||
|
event.dataTransfer.effectAllowed = "all";
|
||||||
|
event.dataTransfer.dropEffect = "move";
|
||||||
|
event.dataTransfer.setDragImage(generateDragElement([{ icon: ClientIcon.ChannelGreen, name: props.channelName }]), 0, 6);
|
||||||
|
setupDragData(event.dataTransfer, props.handlerId, [
|
||||||
|
{
|
||||||
|
type: "channel",
|
||||||
|
channelId: props.channelId
|
||||||
|
}
|
||||||
|
], "channel");
|
||||||
|
event.dataTransfer.setData("text/plain", props.channelName);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{props.channelName}
|
{props.channelName}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {EventHandler, Registry} from "tc-shared/events";
|
import {EventHandler, Registry} from "tc-shared/events";
|
||||||
import {
|
import {
|
||||||
ChannelEntryInfo,
|
ChannelEntryInfo,
|
||||||
ChannelIcons,
|
ChannelIcons, ChannelTreeDragEntry,
|
||||||
ChannelTreeUIEvents,
|
ChannelTreeUIEvents,
|
||||||
ClientIcons,
|
ClientIcons,
|
||||||
ClientNameInfo,
|
ClientNameInfo,
|
||||||
|
@ -22,7 +22,13 @@ import {
|
||||||
RendererClient
|
RendererClient
|
||||||
} from "tc-shared/ui/tree/RendererClient";
|
} from "tc-shared/ui/tree/RendererClient";
|
||||||
import {ServerRenderer} from "tc-shared/ui/tree/RendererServer";
|
import {ServerRenderer} from "tc-shared/ui/tree/RendererServer";
|
||||||
import {generateDragElement, getDragInfo, parseDragData, setupDragData} from "tc-shared/ui/tree/DragHelper";
|
import {
|
||||||
|
DragImageEntryType,
|
||||||
|
generateDragElement,
|
||||||
|
getDragInfo,
|
||||||
|
parseDragData,
|
||||||
|
setupDragData
|
||||||
|
} from "tc-shared/ui/tree/DragHelper";
|
||||||
import {createErrorModal} from "tc-shared/ui/elements/Modal";
|
import {createErrorModal} from "tc-shared/ui/elements/Modal";
|
||||||
|
|
||||||
function isEquivalent(a, b) {
|
function isEquivalent(a, b) {
|
||||||
|
@ -64,6 +70,25 @@ function isEquivalent(a, b) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateDragElementFromRdp(entries: RDPEntry[]) : HTMLElement {
|
||||||
|
return generateDragElement(entries.map<DragImageEntryType>(entry => {
|
||||||
|
if(entry instanceof RDPClient) {
|
||||||
|
return { name: entry.name?.name, icon: entry.status };
|
||||||
|
} else if(entry instanceof RDPChannel) {
|
||||||
|
return { name: entry.info?.name, icon: entry.icon };
|
||||||
|
} else if(entry instanceof RDPServer) {
|
||||||
|
switch (entry.state.state) {
|
||||||
|
case "connected":
|
||||||
|
return { name: entry.state.name, icon: ClientIcon.ServerGreen };
|
||||||
|
case "disconnected":
|
||||||
|
return { name: tr("Not connected"), icon: ClientIcon.ServerGreen };
|
||||||
|
case "connecting":
|
||||||
|
return { name: tr("Connecting"), icon: ClientIcon.ServerGreen };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* auto := Select/unselect/add/remove depending on the selected state & shift key state
|
* auto := Select/unselect/add/remove depending on the selected state & shift key state
|
||||||
* exclusive := Only selected these entries
|
* exclusive := Only selected these entries
|
||||||
|
@ -476,8 +501,38 @@ export class RDPChannelTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
event.dataTransfer.dropEffect = "move";
|
event.dataTransfer.dropEffect = "move";
|
||||||
event.dataTransfer.setDragImage(generateDragElement(entries), 0, 6);
|
event.dataTransfer.setDragImage(generateDragElementFromRdp(entries), 0, 6);
|
||||||
setupDragData(event.dataTransfer, this, entries, dragType);
|
setupDragData(event.dataTransfer, this.handlerId, entries.map<ChannelTreeDragEntry>(entry => {
|
||||||
|
if(entry instanceof RDPClient) {
|
||||||
|
return {
|
||||||
|
type: "client",
|
||||||
|
uniqueTreeId: entry.entryId
|
||||||
|
};
|
||||||
|
} else if(entry instanceof RDPChannel) {
|
||||||
|
return {
|
||||||
|
type: "channel",
|
||||||
|
uniqueTreeId: entry.entryId
|
||||||
|
};
|
||||||
|
} else if(entry instanceof RDPServer) {
|
||||||
|
return {
|
||||||
|
type: "server",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}).filter(entry => !!entry), dragType);
|
||||||
|
|
||||||
|
{
|
||||||
|
let texts = [];
|
||||||
|
for(const entry of entries) {
|
||||||
|
if(entry instanceof RDPClient) {
|
||||||
|
texts.push(entry.name?.name);
|
||||||
|
} else if(entry instanceof RDPChannel) {
|
||||||
|
texts.push(entry.info?.name);
|
||||||
|
} else if(entry instanceof RDPServer) {
|
||||||
|
texts.push(entry.state.state === "connected" ? entry.state.name : undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event.dataTransfer.setData("text/plain", texts.filter(e => !!e).join(", "));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -565,7 +620,7 @@ export class RDPChannelTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.events.fire("action_move_clients", {
|
this.events.fire("action_move_clients", {
|
||||||
entries: data.entryIds,
|
entries: data.entries,
|
||||||
targetTreeEntry: target.entryId
|
targetTreeEntry: target.entryId
|
||||||
});
|
});
|
||||||
} else if(data.type === "channel") {
|
} else if(data.type === "channel") {
|
||||||
|
@ -577,14 +632,14 @@ export class RDPChannelTree {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(data.entryIds.indexOf(target.entryId) !== -1) {
|
if(data.entries.findIndex(entry => entry.type === "channel" && "uniqueTreeId" in entry && entry.uniqueTreeId === target.entryId) !== -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.events.fire("action_move_channels", {
|
this.events.fire("action_move_channels", {
|
||||||
targetTreeEntry: target.entryId,
|
targetTreeEntry: target.entryId,
|
||||||
mode: currentDragHint === "contain" ? "child" : currentDragHint === "top" ? "before" : "after",
|
mode: currentDragHint === "contain" ? "child" : currentDragHint === "top" ? "before" : "after",
|
||||||
entries: data.entryIds
|
entries: data.entries
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue