Added the ability to add custom context menus

canary
WolverinDEV 2019-06-30 16:03:28 +02:00
parent c42d890c7f
commit 40ca24f075
12 changed files with 442 additions and 328 deletions

View File

@ -397,6 +397,57 @@ function main() {
} }
} }
$(document).on('contextmenu', event => {
if(event.isDefaultPrevented())
return;
event.preventDefault();
contextmenu.spawn_context_menu(event.pageX, event.pageY, {
name: 'Test item 1',
type: contextmenu.MenuEntryType.ENTRY,
callback: () => console.log("Test 1 item clicked!"),
disabled: true
}, {
name: 'Test item 2',
type: contextmenu.MenuEntryType.ENTRY,
callback: () => console.log("Test 2 item clicked!")
}, {
name: 'Test item 3',
type: contextmenu.MenuEntryType.SUB_MENU,
callback: () => console.log("Test 3 item clicked!"),
sub_menu: [
{
name: 'Test sub item 1',
type: contextmenu.MenuEntryType.ENTRY,
callback: () => console.log("Test sub 1 item clicked!")
}, {
name: 'Test sub item 2',
type: contextmenu.MenuEntryType.ENTRY,
callback: () => console.log("Test sub 2 item clicked!")
}, {
name: 'Test sub item 4',
type: contextmenu.MenuEntryType.ENTRY,
callback: () => console.log("Test sub 3 item clicked!")
}
]
},
contextmenu.Entry.HR(),
{
name: 'Test item 4',
type: contextmenu.MenuEntryType.ENTRY,
callback: () => console.log("Test 4 item clicked!")
}, {
name: 'Test item 5',
type: contextmenu.MenuEntryType.CHECKBOX,
callback: () => console.log("Test 5 item clicked!")
}, {
name: 'Test item 5',
type: contextmenu.MenuEntryType.CHECKBOX,
callback: () => console.log("Test 5 item clicked!"),
checkbox_checked: true
}, contextmenu.Entry.CLOSE(() => console.log("Menu closed!")))
})
} }
const task_teaweb_starter: loader.Task = { const task_teaweb_starter: loader.Task = {

View File

@ -452,58 +452,60 @@ class ChannelEntry {
} }
let trigger_close = true; let trigger_close = true;
spawn_context_menu(x, y, {
type: MenuEntryType.ENTRY, const bold = text => contextmenu.get_provider().html_format_enabled() ? "<b>" + text + "</b>" : text;
contextmenu.spawn_context_menu(x, y, {
type: contextmenu.MenuEntryType.ENTRY,
name: tr("Show channel info"), name: tr("Show channel info"),
callback: () => { callback: () => {
trigger_close = false; trigger_close = false;
this.channelTree.client.select_info.open_popover() this.channelTree.client.select_info.open_popover()
}, },
icon: "client-about", icon_class: "client-about",
visible: this.channelTree.client.select_info.is_popover() visible: this.channelTree.client.select_info.is_popover()
}, { }, {
type: MenuEntryType.HR, type: contextmenu.MenuEntryType.HR,
visible: this.channelTree.client.select_info.is_popover(), visible: this.channelTree.client.select_info.is_popover(),
name: '' name: ''
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-channel_switch", icon_class: "client-channel_switch",
name: tr("<b>Switch to channel</b>"), name: bold(tr("Switch to channel")),
callback: () => this.joinChannel() callback: () => this.joinChannel()
}, },
...(() => { ...(() => {
const local_client = this.channelTree.client.getClient(); const local_client = this.channelTree.client.getClient();
if (!local_client || local_client.currentChannel() !== this) if (!local_client || local_client.currentChannel() !== this)
return [ return [
MenuEntry.HR(), contextmenu.Entry.HR(),
{ {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-subscribe_to_channel", icon: "client-subscribe_to_channel",
name: tr("<b>Subscribe to channel</b>"), name: bold(tr("Subscribe to channel")),
callback: () => this.subscribe(), callback: () => this.subscribe(),
visible: !this.flag_subscribed visible: !this.flag_subscribed
}, },
{ {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-channel_unsubscribed", icon: "client-channel_unsubscribed",
name: tr("<b>Unsubscribe from channel</b>"), name: bold(tr("Unsubscribe from channel")),
callback: () => this.unsubscribe(), callback: () => this.unsubscribe(),
visible: this.flag_subscribed visible: this.flag_subscribed
}, },
{ {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-subscribe_mode", icon: "client-subscribe_mode",
name: tr("<b>Use inherited subscribe mode</b>"), name: bold(tr("Use inherited subscribe mode")),
callback: () => this.unsubscribe(true), callback: () => this.unsubscribe(true),
visible: this.subscribe_mode != ChannelSubscribeMode.INHERITED visible: this.subscribe_mode != ChannelSubscribeMode.INHERITED
} }
]; ];
return []; return [];
})(), })(),
MenuEntry.HR(), contextmenu.Entry.HR(),
{ {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-channel_edit", icon_class: "client-channel_edit",
name: tr("Edit channel"), name: tr("Edit channel"),
invalidPermission: !channelModify, invalidPermission: !channelModify,
callback: () => { callback: () => {
@ -536,8 +538,8 @@ class ChannelEntry {
} }
}, },
{ {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-channel_delete", icon_class: "client-channel_delete",
name: tr("Delete channel"), name: tr("Delete channel"),
invalidPermission: !flagDelete, invalidPermission: !flagDelete,
callback: () => { callback: () => {
@ -546,10 +548,10 @@ class ChannelEntry {
}) })
} }
}, },
MenuEntry.HR(), contextmenu.Entry.HR(),
{ {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-addon-collection", icon_class: "client-addon-collection",
name: tr("Create music bot"), name: tr("Create music bot"),
callback: () => { callback: () => {
this.channelTree.client.serverConnection.send_command("musicbotcreate", {cid: this.channelId}).then(() => { this.channelTree.client.serverConnection.send_command("musicbotcreate", {cid: this.channelId}).then(() => {
@ -563,21 +565,21 @@ class ChannelEntry {
}); });
} }
}, },
MenuEntry.HR(), contextmenu.Entry.HR(),
{ {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-channel_create_sub", icon_class: "client-channel_create_sub",
name: tr("Create sub channel"), name: tr("Create sub channel"),
invalidPermission: !(channelCreate && this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_CHILD).granted(1)), invalidPermission: !(channelCreate && this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_CHILD).granted(1)),
callback: () => this.channelTree.spawnCreateChannel(this) callback: () => this.channelTree.spawnCreateChannel(this)
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-channel_create", icon_class: "client-channel_create",
name: tr("Create channel"), name: tr("Create channel"),
invalidPermission: !channelCreate, invalidPermission: !channelCreate,
callback: () => this.channelTree.spawnCreateChannel() callback: () => this.channelTree.spawnCreateChannel()
}, },
MenuEntry.CLOSE(() => (trigger_close ? on_close : () => {})()) contextmenu.Entry.CLOSE(() => (trigger_close ? on_close : () => {})())
); );
} }

View File

@ -169,20 +169,15 @@ class ClientEntry {
}); });
} }
protected assignment_context() : ContextMenuEntry[] { protected assignment_context() : contextmenu.MenuEntry[] {
let server_groups: ContextMenuEntry[] = []; let server_groups: contextmenu.MenuEntry[] = [];
for(let group of this.channelTree.client.groups.serverGroups.sort(GroupManager.sorter())) { for(let group of this.channelTree.client.groups.serverGroups.sort(GroupManager.sorter())) {
if(group.type != GroupType.NORMAL) continue; if(group.type != GroupType.NORMAL) continue;
let entry: ContextMenuEntry = {} as any; let entry: contextmenu.MenuEntry = {} as any;
{
let tag = $.spawn("label").addClass("checkbox");
$.spawn("input").attr("type", "checkbox").prop("checked", this.groupAssigned(group)).appendTo(tag);
$.spawn("span").addClass("checkmark").appendTo(tag);
entry.icon = tag;
}
//TODO: May add the server group icon?
entry.checkbox_checked = this.groupAssigned(group);
entry.name = group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]"; entry.name = group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]";
if(this.groupAssigned(group)) { if(this.groupAssigned(group)) {
entry.callback = () => { entry.callback = () => {
@ -201,21 +196,19 @@ class ClientEntry {
}; };
entry.disabled = !this.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_REMOVE_POWER).granted(group.requiredMemberAddPower); entry.disabled = !this.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_REMOVE_POWER).granted(group.requiredMemberAddPower);
} }
entry.type = MenuEntryType.ENTRY; entry.type = contextmenu.MenuEntryType.CHECKBOX;
server_groups.push(entry); server_groups.push(entry);
} }
let channel_groups: ContextMenuEntry[] = []; let channel_groups: contextmenu.MenuEntry[] = [];
for(let group of this.channelTree.client.groups.channelGroups.sort(GroupManager.sorter())) { for(let group of this.channelTree.client.groups.channelGroups.sort(GroupManager.sorter())) {
if(group.type != GroupType.NORMAL) continue; if(group.type != GroupType.NORMAL) continue;
let entry: ContextMenuEntry = {} as any; let entry: contextmenu.MenuEntry = {} as any;
{
let tag = $.spawn("label").addClass("checkbox"); //TODO: May add the channel group icon?
$.spawn("input").attr("type", "checkbox").prop("checked", this.assignedChannelGroup() == group.id).appendTo(tag); entry.checkbox_checked = this.assignedChannelGroup() == group.id;
$.spawn("span").addClass("checkmark").appendTo(tag);
entry.icon = tag;
}
entry.name = group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]"; entry.name = group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]";
entry.callback = () => { entry.callback = () => {
this.channelTree.client.serverConnection.send_command("setclientchannelgroup", { this.channelTree.client.serverConnection.send_command("setclientchannelgroup", {
@ -225,17 +218,17 @@ class ClientEntry {
}); });
}; };
entry.disabled = !this.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_ADD_POWER).granted(group.requiredMemberRemovePower); entry.disabled = !this.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_ADD_POWER).granted(group.requiredMemberRemovePower);
entry.type = MenuEntryType.ENTRY; entry.type = contextmenu.MenuEntryType.CHECKBOX;
channel_groups.push(entry); channel_groups.push(entry);
} }
return [{ return [{
type: MenuEntryType.SUB_MENU, type: contextmenu.MenuEntryType.SUB_MENU,
icon: "client-permission_server_groups", icon_class: "client-permission_server_groups",
name: tr("Set server group"), name: tr("Set server group"),
sub_menu: [ sub_menu: [
{ {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-permission_server_groups", icon: "client-permission_server_groups",
name: "Server groups dialog", name: "Server groups dialog",
callback: () => { callback: () => {
@ -253,19 +246,19 @@ class ClientEntry {
}); });
} }
}, },
MenuEntry.HR(), contextmenu.Entry.HR(),
...server_groups ...server_groups
] ]
},{ },{
type: MenuEntryType.SUB_MENU, type: contextmenu.MenuEntryType.SUB_MENU,
icon: "client-permission_channel", icon_class: "client-permission_channel",
name: tr("Set channel group"), name: tr("Set channel group"),
sub_menu: [ sub_menu: [
...channel_groups ...channel_groups
] ]
},{ },{
type: MenuEntryType.SUB_MENU, type: contextmenu.MenuEntryType.SUB_MENU,
icon: "client-permission_client", icon_class: "client-permission_client",
name: tr("Permissions"), name: tr("Permissions"),
disabled: true, disabled: true,
sub_menu: [ ] sub_menu: [ ]
@ -274,31 +267,33 @@ class ClientEntry {
showContextMenu(x: number, y: number, on_close: () => void = undefined) { showContextMenu(x: number, y: number, on_close: () => void = undefined) {
let trigger_close = true; let trigger_close = true;
spawn_context_menu(x, y, contextmenu.spawn_context_menu(x, y,
{ {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
name: tr("Show client info"), name: tr("Show client info"),
callback: () => { callback: () => {
trigger_close = false; trigger_close = false;
this.channelTree.client.select_info.open_popover() this.channelTree.client.select_info.open_popover()
}, },
icon: "client-about", icon_class: "client-about",
visible: this.channelTree.client.select_info.is_popover() visible: this.channelTree.client.select_info.is_popover()
}, { }, {
type: MenuEntryType.HR, type: contextmenu.MenuEntryType.HR,
visible: this.channelTree.client.select_info.is_popover(), visible: this.channelTree.client.select_info.is_popover(),
name: '' name: ''
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-change_nickname", icon_class: "client-change_nickname",
name: tr("<b>Open text chat</b>"), name: (contextmenu.get_provider().html_format_enabled() ? "<b>" : "") +
tr("Open text chat") +
(contextmenu.get_provider().html_format_enabled() ? "</b>" : ""),
callback: () => { callback: () => {
this.channelTree.client.chat.activeChat = this.chat(true); this.channelTree.client.chat.activeChat = this.chat(true);
this.channelTree.client.chat.focus(); this.channelTree.client.chat.focus();
} }
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-poke", icon_class: "client-poke",
name: tr("Poke client"), name: tr("Poke client"),
callback: () => { callback: () => {
createInputModal(tr("Poke client"), tr("Poke message:<br>"), text => true, result => { createInputModal(tr("Poke client"), tr("Poke message:<br>"), text => true, result => {
@ -314,8 +309,8 @@ class ClientEntry {
}, { width: 400, maxLength: 512 }).open(); }, { width: 400, maxLength: 512 }).open();
} }
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-edit", icon_class: "client-edit",
name: tr("Change description"), name: tr("Change description"),
callback: () => { callback: () => {
createInputModal(tr("Change client description"), tr("New description:<br>"), text => true, result => { createInputModal(tr("Change client description"), tr("New description:<br>"), text => true, result => {
@ -331,11 +326,11 @@ class ClientEntry {
}, { width: 400, maxLength: 1024 }).open(); }, { width: 400, maxLength: 1024 }).open();
} }
}, },
MenuEntry.HR(), contextmenu.Entry.HR(),
...this.assignment_context(), ...this.assignment_context(),
MenuEntry.HR(), { contextmenu.Entry.HR(), {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-move_client_to_own_channel", icon_class: "client-move_client_to_own_channel",
name: tr("Move client to your channel"), name: tr("Move client to your channel"),
callback: () => { callback: () => {
this.channelTree.client.serverConnection.send_command("clientmove", { this.channelTree.client.serverConnection.send_command("clientmove", {
@ -344,8 +339,8 @@ class ClientEntry {
}); });
} }
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-kick_channel", icon_class: "client-kick_channel",
name: tr("Kick client from channel"), name: tr("Kick client from channel"),
callback: () => { callback: () => {
createInputModal(tr("Kick client from channel"), tr("Kick reason:<br>"), text => true, result => { createInputModal(tr("Kick client from channel"), tr("Kick reason:<br>"), text => true, result => {
@ -362,8 +357,8 @@ class ClientEntry {
}, { width: 400, maxLength: 255 }).open(); }, { width: 400, maxLength: 255 }).open();
} }
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-kick_server", icon_class: "client-kick_server",
name: tr("Kick client fom server"), name: tr("Kick client fom server"),
callback: () => { callback: () => {
createInputModal(tr("Kick client from server"), tr("Kick reason:<br>"), text => true, result => { createInputModal(tr("Kick client from server"), tr("Kick reason:<br>"), text => true, result => {
@ -380,8 +375,8 @@ class ClientEntry {
}, { width: 400, maxLength: 255 }).open(); }, { width: 400, maxLength: 255 }).open();
} }
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-ban_client", icon_class: "client-ban_client",
name: tr("Ban client"), name: tr("Ban client"),
invalidPermission: !this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1), invalidPermission: !this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1),
callback: () => { callback: () => {
@ -398,7 +393,7 @@ class ClientEntry {
}); });
} }
}, },
MenuEntry.HR(), contextmenu.Entry.HR(),
/* /*
{ {
type: MenuEntryType.ENTRY, type: MenuEntryType.ENTRY,
@ -418,8 +413,8 @@ class ClientEntry {
MenuEntry.HR(), MenuEntry.HR(),
*/ */
{ {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-volume", icon_class: "client-volume",
name: tr("Change Volume"), name: tr("Change Volume"),
callback: () => { callback: () => {
Modals.spawnChangeVolume(this._audio_handle.get_volume(), volume => { Modals.spawnChangeVolume(this._audio_handle.get_volume(), volume => {
@ -430,7 +425,7 @@ class ClientEntry {
}); });
} }
}, },
MenuEntry.CLOSE(() => (trigger_close ? on_close : () => {})()) contextmenu.Entry.CLOSE(() => (trigger_close ? on_close : () => {})())
); );
} }
@ -839,15 +834,18 @@ class LocalClientEntry extends ClientEntry {
showContextMenu(x: number, y: number, on_close: () => void = undefined): void { showContextMenu(x: number, y: number, on_close: () => void = undefined): void {
const _self = this; const _self = this;
spawn_context_menu(x, y, contextmenu.spawn_context_menu(x, y,
{ {
name: tr("<b>Change name</b>"),
icon: "client-change_nickname", name: (contextmenu.get_provider().html_format_enabled() ? "<b>" : "") +
tr("Change name") +
(contextmenu.get_provider().html_format_enabled() ? "</b>" : ""),
icon_class: "client-change_nickname",
callback: () =>_self.openRename(), callback: () =>_self.openRename(),
type: MenuEntryType.ENTRY type: contextmenu.MenuEntryType.ENTRY
}, { }, {
name: tr("Change description"), name: tr("Change description"),
icon: "client-edit", icon_class: "client-edit",
callback: () => { callback: () => {
createInputModal(tr("Change own description"), tr("New description:<br>"), text => true, result => { createInputModal(tr("Change own description"), tr("New description:<br>"), text => true, result => {
if(result) { if(result) {
@ -860,11 +858,11 @@ class LocalClientEntry extends ClientEntry {
} }
}, { width: 400, maxLength: 1024 }).open(); }, { width: 400, maxLength: 1024 }).open();
}, },
type: MenuEntryType.ENTRY type: contextmenu.MenuEntryType.ENTRY
}, },
MenuEntry.HR(), contextmenu.Entry.HR(),
...this.assignment_context(), ...this.assignment_context(),
MenuEntry.CLOSE(on_close) contextmenu.Entry.CLOSE(on_close)
); );
} }
@ -968,23 +966,23 @@ class MusicClientEntry extends ClientEntry {
showContextMenu(x: number, y: number, on_close: () => void = undefined): void { showContextMenu(x: number, y: number, on_close: () => void = undefined): void {
let trigger_close = true; let trigger_close = true;
spawn_context_menu(x, y, contextmenu.spawn_context_menu(x, y,
{ {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
name: tr("Show bot info"), name: tr("Show bot info"),
callback: () => { callback: () => {
trigger_close = false; trigger_close = false;
this.channelTree.client.select_info.open_popover() this.channelTree.client.select_info.open_popover()
}, },
icon: "client-about", icon_class: "client-about",
visible: this.channelTree.client.select_info.is_popover() visible: this.channelTree.client.select_info.is_popover()
}, { }, {
type: MenuEntryType.HR, type: contextmenu.MenuEntryType.HR,
visible: this.channelTree.client.select_info.is_popover(), visible: this.channelTree.client.select_info.is_popover(),
name: '' name: ''
}, { }, {
name: tr("<b>Change bot name</b>"), name: tr("<b>Change bot name</b>"),
icon: "client-change_nickname", icon_class: "client-change_nickname",
disabled: false, disabled: false,
callback: () => { callback: () => {
createInputModal(tr("Change music bots nickname"), tr("New nickname:<br>"), text => text.length >= 3 && text.length <= 31, result => { createInputModal(tr("Change music bots nickname"), tr("New nickname:<br>"), text => text.length >= 3 && text.length <= 31, result => {
@ -997,10 +995,10 @@ class MusicClientEntry extends ClientEntry {
} }
}, { width: 400, maxLength: 255 }).open(); }, { width: 400, maxLength: 255 }).open();
}, },
type: MenuEntryType.ENTRY type: contextmenu.MenuEntryType.ENTRY
}, { }, {
name: tr("Change bot description"), name: tr("Change bot description"),
icon: "client-edit", icon_class: "client-edit",
disabled: false, disabled: false,
callback: () => { callback: () => {
createInputModal(tr("Change music bots description"), tr("New description:<br>"), text => true, result => { createInputModal(tr("Change music bots description"), tr("New description:<br>"), text => true, result => {
@ -1013,7 +1011,7 @@ class MusicClientEntry extends ClientEntry {
} }
}, { width: 400, maxLength: 255 }).open(); }, { width: 400, maxLength: 255 }).open();
}, },
type: MenuEntryType.ENTRY type: contextmenu.MenuEntryType.ENTRY
}, },
/* /*
{ {
@ -1026,7 +1024,7 @@ class MusicClientEntry extends ClientEntry {
*/ */
{ {
name: tr("Open bot's playlist"), name: tr("Open bot's playlist"),
icon: "client-edit", icon_class: "client-edit",
disabled: false, disabled: false,
callback: () => { callback: () => {
this.channelTree.client.serverConnection.command_helper.request_playlist_list().then(lists => { this.channelTree.client.serverConnection.command_helper.request_playlist_list().then(lists => {
@ -1041,11 +1039,11 @@ class MusicClientEntry extends ClientEntry {
createErrorModal(tr("Failed to query playlist."), tr("Failed to query playlist info.")).open(); createErrorModal(tr("Failed to query playlist."), tr("Failed to query playlist info.")).open();
}); });
}, },
type: MenuEntryType.ENTRY type: contextmenu.MenuEntryType.ENTRY
}, },
{ {
name: tr("Quick url replay"), name: tr("Quick url replay"),
icon: "client-edit", icon_class: "client-edit",
disabled: false, disabled: false,
callback: () => { callback: () => {
createInputModal(tr("Please enter the URL"), tr("URL:"), text => true, result => { createInputModal(tr("Please enter the URL"), tr("URL:"), text => true, result => {
@ -1064,13 +1062,13 @@ class MusicClientEntry extends ClientEntry {
} }
}, { width: 400, maxLength: 255 }).open(); }, { width: 400, maxLength: 255 }).open();
}, },
type: MenuEntryType.ENTRY type: contextmenu.MenuEntryType.ENTRY
}, },
MenuEntry.HR(), contextmenu.Entry.HR(),
...super.assignment_context(), ...super.assignment_context(),
MenuEntry.HR(),{ contextmenu.Entry.HR(),{
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-move_client_to_own_channel", icon_class: "client-move_client_to_own_channel",
name: tr("Move client to your channel"), name: tr("Move client to your channel"),
callback: () => { callback: () => {
this.channelTree.client.serverConnection.send_command("clientmove", { this.channelTree.client.serverConnection.send_command("clientmove", {
@ -1079,8 +1077,8 @@ class MusicClientEntry extends ClientEntry {
}); });
} }
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-kick_channel", icon_class: "client-kick_channel",
name: tr("Kick client from channel"), name: tr("Kick client from channel"),
callback: () => { callback: () => {
createInputModal(tr("Kick client from channel"), tr("Kick reason:<br>"), text => true, result => { createInputModal(tr("Kick client from channel"), tr("Kick reason:<br>"), text => true, result => {
@ -1095,10 +1093,10 @@ class MusicClientEntry extends ClientEntry {
}, { width: 400, maxLength: 255 }).open(); }, { width: 400, maxLength: 255 }).open();
} }
}, },
MenuEntry.HR(), contextmenu.Entry.HR(),
{ {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-volume", icon_class: "client-volume",
name: tr("Change local volume"), name: tr("Change local volume"),
callback: () => { callback: () => {
Modals.spawnChangeVolume(this._audio_handle.get_volume(), volume => { Modals.spawnChangeVolume(this._audio_handle.get_volume(), volume => {
@ -1110,8 +1108,8 @@ class MusicClientEntry extends ClientEntry {
} }
}, },
{ {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-volume", icon_class: "client-volume",
name: tr("Change remote volume"), name: tr("Change remote volume"),
callback: () => { callback: () => {
let max_volume = this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_MUSIC_CREATE_MODIFY_MAX_VOLUME).value; let max_volume = this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_MUSIC_CREATE_MODIFY_MAX_VOLUME).value;
@ -1132,10 +1130,10 @@ class MusicClientEntry extends ClientEntry {
}); });
} }
}, },
MenuEntry.HR(), contextmenu.Entry.HR(),
{ {
name: tr("Delete bot"), name: tr("Delete bot"),
icon: "client-delete", icon_class: "client-delete",
disabled: false, disabled: false,
callback: () => { callback: () => {
const tag = $.spawn("div").append(MessageHelper.formatMessage(tr("Do you really want to delete {0}"), this.createChatTag(false))); const tag = $.spawn("div").append(MessageHelper.formatMessage(tr("Do you really want to delete {0}"), this.createChatTag(false)));
@ -1147,9 +1145,9 @@ class MusicClientEntry extends ClientEntry {
} }
}); });
}, },
type: MenuEntryType.ENTRY type: contextmenu.MenuEntryType.ENTRY
}, },
MenuEntry.CLOSE(() => (trigger_close ? on_close : () => {})()) contextmenu.Entry.CLOSE(() => (trigger_close ? on_close : () => {})())
); );
} }

View File

@ -1,142 +1,220 @@
let context_menu: JQuery; namespace contextmenu {
export interface MenuEntry {
callback?: () => void;
type: MenuEntryType;
name: (() => string) | string;
icon_class?: string;
icon_path?: string;
disabled?: boolean;
visible?: boolean;
$(document).bind("click", function (e) { checkbox_checked?: boolean;
let menu = context_menu || (context_menu = $(".context-menu"));
if(!menu.is(":visible")) return; invalidPermission?: boolean;
sub_menu?: MenuEntry[];
}
if ($(e.target).parents(".context-menu").length == 0) { export enum MenuEntryType {
CLOSE,
ENTRY,
CHECKBOX,
HR,
SUB_MENU
}
export class Entry {
static HR() {
return {
callback: () => {},
type: MenuEntryType.HR,
name: "",
icon: ""
};
};
static CLOSE(callback: () => void) {
return {
callback: callback,
type: MenuEntryType.CLOSE,
name: "",
icon: ""
};
}
}
export interface ContextMenuProvider {
despawn_context_menu(); despawn_context_menu();
e.preventDefault(); spawn_context_menu(x: number, y: number, ...entries: MenuEntry[]);
initialize();
finalize();
html_format_enabled() : boolean;
} }
});
let contextMenuCloseFn = undefined; let provider: ContextMenuProvider;
function despawn_context_menu() { export function spawn_context_menu(x: number, y: number, ...entries: MenuEntry[]) {
let menu = context_menu || (context_menu = $(".context-menu")); if(!provider) {
console.error(tr("Failed to spawn context menu! Missing provider!"));
return;
}
if(!menu.is(":visible")) return; provider.spawn_context_menu(x, y, ...entries);
menu.animate({opacity: 0}, 100, () => menu.css("display", "none")); }
if(contextMenuCloseFn) contextMenuCloseFn();
}
enum MenuEntryType { export function despawn_context_menu() {
CLOSE, if(!provider)
ENTRY, return;
HR,
SUB_MENU
}
class MenuEntry { provider.despawn_context_menu();
static HR() { }
return {
callback: () => {},
type: MenuEntryType.HR,
name: "",
icon: ""
};
};
static CLOSE(callback: () => void) { export function get_provider() : ContextMenuProvider { return provider; }
return { export function set_provider(_provider: ContextMenuProvider) {
callback: callback, provider = _provider;
type: MenuEntryType.CLOSE, provider.initialize();
name: "",
icon: ""
};
} }
} }
interface ContextMenuEntry { class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider {
callback?: () => void; private _global_click_listener: (event) => any;
type: MenuEntryType; private _context_menu: JQuery;
name: (() => string) | string; private _close_callbacks: (() => any)[] = [];
icon?: (() => string) | string | JQuery;
disabled?: boolean;
visible?: boolean;
invalidPermission?: boolean; despawn_context_menu() {
sub_menu?: ContextMenuEntry[]; let menu = this._context_menu || (this._context_menu = $(".context-menu"));
}
function generate_tag(entry: ContextMenuEntry) : JQuery { if(!menu.is(":visible"))
if(entry.type == MenuEntryType.HR) { return;
return $.spawn("hr");
} else if(entry.type == MenuEntryType.ENTRY) { menu.animate({opacity: 0}, 100, () => menu.css("display", "none"));
console.log(entry.icon); for(const callback of this._close_callbacks)
let icon = $.isFunction(entry.icon) ? entry.icon() : entry.icon; callback();
if(typeof(icon) === "string") { this._close_callbacks = [];
}
finalize() {
$(document).unbind('click', this._global_click_listener);
}
initialize() {
this._global_click_listener = this.on_global_click.bind(this);
$(document).bind('click', this._global_click_listener);
}
private on_global_click(event) {
let menu = this._context_menu || (this._context_menu = $(".context-menu"));
if(!menu.is(":visible")) return;
if ($(event.target).parents(".context-menu").length == 0) {
this.despawn_context_menu();
event.preventDefault();
}
}
private generate_tag(entry: contextmenu.MenuEntry) : JQuery {
if(entry.type == contextmenu.MenuEntryType.HR) {
return $.spawn("hr");
} else if(entry.type == contextmenu.MenuEntryType.ENTRY) {
let icon = entry.icon_class;
if(!icon || icon.length == 0) icon = "icon_empty"; if(!icon || icon.length == 0) icon = "icon_empty";
else icon = "icon " + icon; else icon = "icon " + icon;
}
let tag = $.spawn("div").addClass("entry"); let tag = $.spawn("div").addClass("entry");
tag.append(typeof(icon) === "string" ? $.spawn("div").addClass(icon) : icon); tag.append($.spawn("div").addClass(icon));
tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name)); tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name));
if(entry.disabled || entry.invalidPermission) tag.addClass("disabled"); if(entry.disabled || entry.invalidPermission) tag.addClass("disabled");
else { else {
tag.click(function () { tag.click( () => {
if($.isFunction(entry.callback)) entry.callback(); if($.isFunction(entry.callback))
despawn_context_menu(); entry.callback();
}); this.despawn_context_menu();
} });
return tag;
} else if(entry.type == MenuEntryType.SUB_MENU) {
let icon = $.isFunction(entry.icon) ? entry.icon() : entry.icon;
if(typeof(icon) === "string") {
if(!icon || icon.length == 0) icon = "icon_empty";
else icon = "icon " + icon;
}
let tag = $.spawn("div").addClass("entry").addClass("sub-container");
tag.append(typeof(icon) === "string" ? $.spawn("div").addClass(icon) : icon);
tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name));
tag.append($.spawn("div").addClass("arrow right"));
if(entry.disabled || entry.invalidPermission) tag.addClass("disabled");
else {
let menu = $.spawn("div").addClass("sub-menu").addClass("context-menu-container");
for(const e of entry.sub_menu) {
if(typeof(entry.visible) === 'boolean' && !entry.visible)
continue;
menu.append(generate_tag(e));
} }
menu.appendTo(tag); return tag;
} else if(entry.type == contextmenu.MenuEntryType.CHECKBOX) {
let checkbox = $.spawn("label").addClass("checkbox");
$.spawn("input").attr("type", "checkbox").prop("checked", !!entry.checkbox_checked).appendTo(checkbox);
$.spawn("span").addClass("checkmark").appendTo(checkbox);
let tag = $.spawn("div").addClass("entry");
tag.append(checkbox);
tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name));
if(entry.disabled || entry.invalidPermission)
tag.addClass("disabled");
else {
tag.click( () => {
if($.isFunction(entry.callback))
entry.callback();
this.despawn_context_menu();
});
}
return tag;
} else if(entry.type == contextmenu.MenuEntryType.SUB_MENU) {
let icon = entry.icon_class;
if(!icon || icon.length == 0) icon = "icon_empty";
else icon = "icon " + icon;
let tag = $.spawn("div").addClass("entry").addClass("sub-container");
tag.append($.spawn("div").addClass(icon));
tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name));
tag.append($.spawn("div").addClass("arrow right"));
if(entry.disabled || entry.invalidPermission) tag.addClass("disabled");
else {
let menu = $.spawn("div").addClass("sub-menu").addClass("context-menu-container");
for(const e of entry.sub_menu) {
if(typeof(entry.visible) === 'boolean' && !entry.visible)
continue;
menu.append(this.generate_tag(e));
}
menu.appendTo(tag);
}
return tag;
} }
return tag; return $.spawn("div").text("undefined");
}
spawn_context_menu(x: number, y: number, ...entries: contextmenu.MenuEntry[]) {
let menu_tag = this._context_menu || (this._context_menu = $(".context-menu"));
menu_tag.finish().empty().css("opacity", "0");
const menu_container = $.spawn("div").addClass("context-menu-container");
this._close_callbacks = [];
for(const entry of entries){
if(typeof(entry.visible) === 'boolean' && !entry.visible)
continue;
if(entry.type == contextmenu.MenuEntryType.CLOSE) {
this._close_callbacks.push(entry.callback);
} else
menu_container.append(this.generate_tag(entry));
}
menu_tag.append(menu_container);
menu_tag.animate({opacity: 1}, 100).css("display", "block");
const width = menu_container.visible_width();
if(x + width + 5 > window.innerWidth)
menu_container.addClass("left");
// In the right position (the mouse)
menu_tag.css({
"top": y + "px",
"left": x + "px"
});
}
html_format_enabled(): boolean {
return true;
} }
return $.spawn("div").text("undefined");
} }
function spawn_context_menu(x, y, ...entries: ContextMenuEntry[]) { //TODO: Improve
let menu_tag = context_menu || (context_menu = $(".context-menu")); if(!window.require)
menu_tag.finish().empty().css("opacity", "0"); contextmenu.set_provider(new HTMLContextMenuProvider());
const menu_container = $.spawn("div").addClass("context-menu-container");
contextMenuCloseFn = undefined;
for(const entry of entries){
if(typeof(entry.visible) === 'boolean' && !entry.visible)
continue;
if(entry.type == MenuEntryType.CLOSE) {
contextMenuCloseFn = entry.callback;
} else
menu_container.append(generate_tag(entry));
}
menu_tag.append(menu_container);
menu_tag.animate({opacity: 1}, 100).css("display", "block");
const width = menu_container.visible_width();
if(x + width + 5 > window.innerWidth)
menu_container.addClass("left");
// In the right position (the mouse)
menu_tag.css({
"top": y + "px",
"left": x + "px"
});
}

View File

@ -541,17 +541,17 @@ class ControlBar {
return; return;
event.preventDefault(); event.preventDefault();
spawn_context_menu(event.pageX, event.pageY, { contextmenu.spawn_context_menu(event.pageX, event.pageY, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
name: tr("Connect"), name: tr("Connect"),
icon: 'client-connect', icon_class: 'client-connect',
callback: () => bookmark_connect(false) callback: () => bookmark_connect(false)
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
name: tr("Connect in a new tab"), name: tr("Connect in a new tab"),
icon: 'client-connect', icon_class: 'client-connect',
callback: () => bookmark_connect(true) callback: () => bookmark_connect(true)
}, MenuEntry.CLOSE(() => { }, contextmenu.Entry.CLOSE(() => {
setTimeout(() => { setTimeout(() => {
this.htmlTag.find(".btn_bookmark.button-dropdown").removeClass("force-show") this.htmlTag.find(".btn_bookmark.button-dropdown").removeClass("force-show")
}, 250); }, 250);

View File

@ -455,7 +455,7 @@ class ChannelInfoManager extends InfoManager<ChannelEntry> {
properties["channel_name"] = channel.generate_tag(false); properties["channel_name"] = channel.generate_tag(false);
properties["channel_type"] = ChannelType.normalize(channel.channelType()); properties["channel_type"] = ChannelType.normalize(channel.channelType());
properties["channel_clients"] = channel.channelTree.clientsByChannel(channel).length; properties["channel_clients"] = channel.channelTree.clientsByChannel(channel).length;
properties["channel_subscribed"] = true; //TODO properties["channel_subscribed"] = channel.flag_subscribed;
properties["server_encryption"] = channel.channelTree.server.properties.virtualserver_codec_encryption_mode; properties["server_encryption"] = channel.channelTree.server.properties.virtualserver_codec_encryption_mode;
for(let key in channel.properties) for(let key in channel.properties)
@ -465,16 +465,15 @@ class ChannelInfoManager extends InfoManager<ChannelEntry> {
properties["bbcode_channel_description"] = tag_channel_description; properties["bbcode_channel_description"] = tag_channel_description;
channel.getChannelDescription().then(description => { channel.getChannelDescription().then(description => {
let result = XBBCODE.process({ const result = xbbcode.parse(description, {});
text: description, /*
escapeHtml: true,
addInLineBreaks: true
});
if(result.error) { if(result.error) {
console.log("BBCode parse error: %o", result.errorQueue); console.log("BBCode parse error: %o", result.errorQueue);
} }
*/
tag_channel_description.html(result.html) tag_channel_description.empty()
.append($.spawn("div").html(result.build_html()).contents())
.css("overflow-y", "auto") .css("overflow-y", "auto")
.css("flex-grow", "1"); .css("flex-grow", "1");
}); });

View File

@ -245,10 +245,10 @@ class ChatEntry {
tag.on("contextmenu", (e) => { tag.on("contextmenu", (e) => {
e.preventDefault(); e.preventDefault();
let actions: ContextMenuEntry[] = []; let actions: contextmenu.MenuEntry[] = [];
actions.push({ actions.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "", icon_class: "",
name: tr("Clear"), name: tr("Clear"),
callback: () => { callback: () => {
this.history = []; this.history = [];
@ -257,23 +257,23 @@ class ChatEntry {
}); });
if(this.flag_closeable) { if(this.flag_closeable) {
actions.push({ actions.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-tab_close_button", icon_class: "client-tab_close_button",
name: tr("Close"), name: tr("Close"),
callback: () => this.handle.deleteChat(this) callback: () => this.handle.deleteChat(this)
}); });
} }
actions.push({ actions.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-tab_close_button", icon_class: "client-tab_close_button",
name: tr("Close all private tabs"), name: tr("Close all private tabs"),
callback: () => { callback: () => {
//TODO Implement this? //TODO Implement this?
}, },
visible: false visible: false
}); });
spawn_context_menu(e.pageX, e.pageY, ...actions); contextmenu.spawn_context_menu(e.pageX, e.pageY, ...actions);
}); });
tag_close.click(() => { tag_close.click(() => {

View File

@ -181,24 +181,20 @@ namespace unused {
if(event.isDefaultPrevented()) return; if(event.isDefaultPrevented()) return;
event.preventDefault(); event.preventDefault();
spawn_context_menu(event.pageX, event.pageY, { contextmenu.spawn_context_menu(event.pageX, event.pageY, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "",
name: tr("Expend group"), name: tr("Expend group"),
callback: () => update_collapse_status(true, false) callback: () => update_collapse_status(true, false)
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "",
name: tr("Expend all"), name: tr("Expend all"),
callback: () => update_collapse_status(true, true) callback: () => update_collapse_status(true, true)
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "",
name: tr("Collapse group"), name: tr("Collapse group"),
callback: () => update_collapse_status(false, false) callback: () => update_collapse_status(false, false)
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "",
name: tr("Collapse all"), name: tr("Collapse all"),
callback: () => update_collapse_status(false, true) callback: () => update_collapse_status(false, true)
}); });

View File

@ -130,14 +130,12 @@ namespace Modals {
if(event.isDefaultPrevented()) return; if(event.isDefaultPrevented()) return;
event.preventDefault(); event.preventDefault();
spawn_context_menu(event.pageX, event.pageY, { contextmenu.spawn_context_menu(event.pageX, event.pageY, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "",
name: tr("Expend all"), name: tr("Expend all"),
callback: () => this.entry_editor.expend_all() callback: () => this.entry_editor.expend_all()
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "",
name: tr("Collapse all"), name: tr("Collapse all"),
callback: () => this.entry_editor.collapse_all() callback: () => this.entry_editor.collapse_all()
}); });
@ -221,18 +219,16 @@ namespace Modals {
}; };
entry.on_context_menu = (x, y) => { entry.on_context_menu = (x, y) => {
let entries: ContextMenuEntry[] = []; let entries: contextmenu.MenuEntry[] = [];
if(typeof(entry.value) === "undefined") { if(typeof(entry.value) === "undefined") {
entries.push({ entries.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "",
name: tr("Add permission"), name: tr("Add permission"),
callback: () => entry.trigger_value_assign() callback: () => entry.trigger_value_assign()
}); });
} else { } else {
entries.push({ entries.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "",
name: tr("Remove permission"), name: tr("Remove permission"),
callback: () => { callback: () => {
entry.value = undefined; entry.value = undefined;
@ -243,15 +239,13 @@ namespace Modals {
if(typeof(entry.granted) === "undefined") { if(typeof(entry.granted) === "undefined") {
entries.push({ entries.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "",
name: tr("Add grant permission"), name: tr("Add grant permission"),
callback: () => entry.trigger_grant_assign() callback: () => entry.trigger_grant_assign()
}); });
} else { } else {
entries.push({ entries.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "",
name: tr("Remove grant permission"), name: tr("Remove grant permission"),
callback: () => { callback: () => {
entry.granted = undefined; entry.granted = undefined;
@ -259,23 +253,20 @@ namespace Modals {
} }
}); });
} }
entries.push(MenuEntry.HR()); entries.push(contextmenu.Entry.HR());
entries.push({ entries.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "",
name: tr("Expend all"), name: tr("Expend all"),
callback: () => this.entry_editor.expend_all() callback: () => this.entry_editor.expend_all()
}); });
entries.push({ entries.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "",
name: tr("Collapse all"), name: tr("Collapse all"),
callback: () => this.entry_editor.collapse_all() callback: () => this.entry_editor.collapse_all()
}); });
entries.push(MenuEntry.HR()); entries.push(contextmenu.Entry.HR());
entries.push({ entries.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "",
name: tr("Show permission description"), name: tr("Show permission description"),
callback: () => { callback: () => {
createInfoModal( createInfoModal(
@ -285,15 +276,14 @@ namespace Modals {
} }
}); });
entries.push({ entries.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "",
name: tr("Copy permission name"), name: tr("Copy permission name"),
callback: () => { callback: () => {
copy_to_clipboard(permission.name); copy_to_clipboard(permission.name);
} }
}); });
spawn_context_menu(x, y, ...entries); contextmenu.spawn_context_menu(x, y, ...entries);
} }
} }
} }
@ -1220,10 +1210,10 @@ namespace Modals {
return; return;
event.preventDefault(); event.preventDefault();
spawn_context_menu(event.pageX, event.pageY, { contextmenu.spawn_context_menu(event.pageX, event.pageY, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
name: tr("Remove client"), name: tr("Remove client"),
icon: 'client-delete', icon_class: 'client-delete',
callback: () => { callback: () => {
connection.serverConnection.send_command("servergroupdelclient", { connection.serverConnection.send_command("servergroupdelclient", {
sgid: current_group.id, sgid: current_group.id,
@ -1233,9 +1223,9 @@ namespace Modals {
}); });
} }
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
name: tr("Copy unique id"), name: tr("Copy unique id"),
icon: 'client-copy', icon_class: 'client-copy',
callback: () => copy_to_clipboard(client.client_unique_identifier) callback: () => copy_to_clipboard(client.client_unique_identifier)
}) })
}); });

View File

@ -136,22 +136,22 @@ class ServerEntry {
spawnContextMenu(x: number, y: number, on_close: () => void = () => {}) { spawnContextMenu(x: number, y: number, on_close: () => void = () => {}) {
let trigger_close = true; let trigger_close = true;
spawn_context_menu(x, y, { contextmenu.spawn_context_menu(x, y, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
name: tr("Show server info"), name: tr("Show server info"),
callback: () => { callback: () => {
trigger_close = false; trigger_close = false;
this.channelTree.client.select_info.open_popover() this.channelTree.client.select_info.open_popover()
}, },
icon: "client-about", icon_class: "client-about",
visible: this.channelTree.client.select_info.is_popover() visible: this.channelTree.client.select_info.is_popover()
}, { }, {
type: MenuEntryType.HR, type: contextmenu.MenuEntryType.HR,
visible: this.channelTree.client.select_info.is_popover(), visible: this.channelTree.client.select_info.is_popover(),
name: '' name: ''
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-virtualserver_edit", icon_class: "client-virtualserver_edit",
name: tr("Edit"), name: tr("Edit"),
callback: () => { callback: () => {
Modals.createServerModal(this, properties => { Modals.createServerModal(this, properties => {
@ -164,22 +164,22 @@ class ServerEntry {
}); });
} }
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-iconviewer", icon_class: "client-iconviewer",
name: tr("View icons"), name: tr("View icons"),
callback: () => Modals.spawnIconSelect(this.channelTree.client) callback: () => Modals.spawnIconSelect(this.channelTree.client)
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: 'client-iconsview', icon_class: 'client-iconsview',
name: tr("View avatars"), name: tr("View avatars"),
callback: () => Modals.spawnAvatarList(this.channelTree.client) callback: () => Modals.spawnAvatarList(this.channelTree.client)
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-invite_buddy", icon_class: "client-invite_buddy",
name: tr("Invite buddy"), name: tr("Invite buddy"),
callback: () => Modals.spawnInviteEditor(this.channelTree.client) callback: () => Modals.spawnInviteEditor(this.channelTree.client)
}, },
MenuEntry.CLOSE(() => (trigger_close ? on_close : () => {})()) contextmenu.Entry.CLOSE(() => (trigger_close ? on_close : () => {})())
); );
} }

View File

@ -90,15 +90,15 @@ class ChannelTree {
this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT).granted(1) || this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT).granted(1) ||
this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_PERMANENT).granted(1); this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_PERMANENT).granted(1);
spawn_context_menu(x, y, contextmenu.spawn_context_menu(x, y,
{ {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-channel_create", icon_class: "client-channel_create",
name: tr("Create channel"), name: tr("Create channel"),
invalidPermission: !channelCreate, invalidPermission: !channelCreate,
callback: () => this.spawnCreateChannel() callback: () => this.spawnCreateChannel()
}, },
MenuEntry.CLOSE(on_close) contextmenu.Entry.CLOSE(on_close)
); );
} }
@ -461,11 +461,11 @@ class ChannelTree {
const music_entry = clients.map(e => e instanceof MusicClientEntry ? 1 : 0).reduce((a, b) => a + b, 0) > 0; const music_entry = clients.map(e => e instanceof MusicClientEntry ? 1 : 0).reduce((a, b) => a + b, 0) > 0;
const local_client = clients.map(e => e instanceof LocalClientEntry ? 1 : 0).reduce((a, b) => a + b, 0) > 0; const local_client = clients.map(e => e instanceof LocalClientEntry ? 1 : 0).reduce((a, b) => a + b, 0) > 0;
console.log(tr("Music only: %o | Container music: %o | Container local: %o"), music_entry, music_entry, local_client); console.log(tr("Music only: %o | Container music: %o | Container local: %o"), music_entry, music_entry, local_client);
let entries: ContextMenuEntry[] = []; let entries: contextmenu.MenuEntry[] = [];
if (!music_entry && !local_client) { //Music bots or local client cant be poked if (!music_entry && !local_client) { //Music bots or local client cant be poked
entries.push({ entries.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-poke", icon_class: "client-poke",
name: tr("Poke clients"), name: tr("Poke clients"),
callback: () => { callback: () => {
createInputModal(tr("Poke clients"), tr("Poke message:<br>"), text => true, result => { createInputModal(tr("Poke clients"), tr("Poke message:<br>"), text => true, result => {
@ -482,8 +482,8 @@ class ChannelTree {
}); });
} }
entries.push({ entries.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-move_client_to_own_channel", icon_class: "client-move_client_to_own_channel",
name: tr("Move clients to your channel"), name: tr("Move clients to your channel"),
callback: () => { callback: () => {
const target = this.client.getClient().currentChannel().getChannelId(); const target = this.client.getClient().currentChannel().getChannelId();
@ -495,10 +495,10 @@ class ChannelTree {
} }
}); });
if (!local_client) {//local client cant be kicked and/or banned or kicked if (!local_client) {//local client cant be kicked and/or banned or kicked
entries.push(MenuEntry.HR()); entries.push(contextmenu.Entry.HR());
entries.push({ entries.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-kick_channel", icon_class: "client-kick_channel",
name: tr("Kick clients from channel"), name: tr("Kick clients from channel"),
callback: () => { callback: () => {
createInputModal(tr("Kick clients from channel"), tr("Kick reason:<br>"), text => true, result => { createInputModal(tr("Kick clients from channel"), tr("Kick reason:<br>"), text => true, result => {
@ -517,8 +517,8 @@ class ChannelTree {
if (!music_entry) { //Music bots cant be banned or kicked if (!music_entry) { //Music bots cant be banned or kicked
entries.push({ entries.push({
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-kick_server", icon_class: "client-kick_server",
name: tr("Kick clients fom server"), name: tr("Kick clients fom server"),
callback: () => { callback: () => {
createInputModal(tr("Kick clients from server"), tr("Kick reason:<br>"), text => true, result => { createInputModal(tr("Kick clients from server"), tr("Kick reason:<br>"), text => true, result => {
@ -534,8 +534,8 @@ class ChannelTree {
}, {width: 400, maxLength: 255}).open(); }, {width: 400, maxLength: 255}).open();
} }
}, { }, {
type: MenuEntryType.ENTRY, type: contextmenu.MenuEntryType.ENTRY,
icon: "client-ban_client", icon_class: "client-ban_client",
name: tr("Ban clients"), name: tr("Ban clients"),
invalidPermission: !this.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1), invalidPermission: !this.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1),
callback: () => { callback: () => {
@ -555,10 +555,10 @@ class ChannelTree {
}); });
} }
if(music_only) { if(music_only) {
entries.push(MenuEntry.HR()); entries.push(contextmenu.Entry.HR());
entries.push({ entries.push({
name: tr("Delete bots"), name: tr("Delete bots"),
icon: "client-delete", icon_class: "client-delete",
disabled: false, disabled: false,
callback: () => { callback: () => {
const param_string = clients.map((_, index) => "{" + index + "}").join(', '); const param_string = clients.map((_, index) => "{" + index + "}").join(', ');
@ -574,11 +574,11 @@ class ChannelTree {
} }
}); });
}, },
type: MenuEntryType.ENTRY type: contextmenu.MenuEntryType.ENTRY
}); });
} }
} }
spawn_context_menu(event.pageX, event.pageY, ...entries); contextmenu.spawn_context_menu(event.pageX, event.pageY, ...entries);
} }
clientsByGroup(group: Group) : ClientEntry[] { clientsByGroup(group: Group) : ClientEntry[] {

2
vendor/xbbcode vendored

@ -1 +1 @@
Subproject commit d9a47d059ae9cce559d7a75553a25ba342d36229 Subproject commit 8092afb59615aa19cff372689cd6985c96e3f2ba