2019-08-21 10:00:01 +02:00
namespace top_menu {
export interface HRItem { }
export interface MenuItem {
append_item ( label : string ) : MenuItem ;
append_hr ( ) : HRItem ;
delete_item ( item : MenuItem | HRItem ) ;
items ( ) : ( MenuItem | HRItem ) [ ] ;
icon ( klass? : string | Promise < Icon > | Icon ) : string ;
label ( value? : string ) : string ;
visible ( value? : boolean ) : boolean ;
disabled ( value? : boolean ) : boolean ;
click ( callback : ( ) = > any ) : this ;
}
export interface MenuBarDriver {
initialize ( ) ;
append_item ( label : string ) : MenuItem ;
delete_item ( item : MenuItem ) ;
items ( ) : MenuItem [ ] ;
flush_changes ( ) ;
}
let _driver : MenuBarDriver ;
export function driver ( ) : MenuBarDriver {
return _driver ;
}
export function set_driver ( driver : MenuBarDriver ) {
_driver = driver ;
}
export interface NativeActions {
open_dev_tools ( ) ;
reload_page ( ) ;
check_native_update ( ) ;
open_change_log ( ) ;
quit ( ) ;
}
export let native_actions : NativeActions ;
namespace html {
class HTMLHrItem implements top_menu . HRItem {
readonly html_tag : JQuery ;
constructor ( ) {
this . html_tag = $ . spawn ( "hr" ) ;
}
}
class HTMLMenuItem implements top_menu . MenuItem {
readonly html_tag : JQuery ;
readonly _label_tag : JQuery ;
readonly _label_icon_tag : JQuery ;
readonly _label_text_tag : JQuery ;
readonly _submenu_tag : JQuery ;
private _items : ( MenuItem | HRItem ) [ ] = [ ] ;
private _label : string ;
private _callback_click : ( ) = > any ;
constructor ( label : string , mode : "side" | "down" ) {
this . _label = label ;
this . html_tag = $ . spawn ( "div" ) . addClass ( "container-menu-item type-" + mode ) ;
this . _label_tag = $ . spawn ( "div" ) . addClass ( "menu-item" ) ;
this . _label_icon_tag = $ . spawn ( "div" ) . addClass ( "container-icon" ) . appendTo ( this . _label_tag ) ;
$ . spawn ( "div" ) . addClass ( "container-label" ) . append (
this . _label_text_tag = $ . spawn ( "a" ) . text ( label )
) . appendTo ( this . _label_tag ) ;
this . _label_tag . on ( 'click' , event = > {
if ( event . isDefaultPrevented ( ) )
return ;
const disabled = this . html_tag . hasClass ( "disabled" ) ;
if ( this . _callback_click && ! disabled ) {
this . _callback_click ( ) ;
}
event . preventDefault ( ) ;
2019-10-25 11:04:18 +02:00
if ( disabled )
event . stopPropagation ( ) ;
2019-08-21 10:00:01 +02:00
} ) ;
this . _submenu_tag = $ . spawn ( "div" ) . addClass ( "sub-menu" ) ;
this . html_tag . append ( this . _label_tag ) ;
this . html_tag . append ( this . _submenu_tag ) ;
}
append_item ( label : string ) : top_menu . MenuItem {
const item = new HTMLMenuItem ( label , "side" ) ;
this . _items . push ( item ) ;
this . _submenu_tag . append ( item . html_tag ) ;
this . html_tag . addClass ( 'sub-entries' ) ;
return item ;
}
append_hr ( ) : HRItem {
const item = new HTMLHrItem ( ) ;
this . _items . push ( item ) ;
this . _submenu_tag . append ( item . html_tag ) ;
return item ;
}
delete_item ( item : top_menu.MenuItem | top_menu . HRItem ) {
this . _items . remove ( item ) ;
( item as any ) . html_tag . detach ( ) ;
this . html_tag . toggleClass ( 'sub-entries' , this . _items . length > 0 ) ;
}
disabled ( value? : boolean ) : boolean {
if ( typeof ( value ) === "undefined" )
return this . html_tag . hasClass ( "disabled" ) ;
this . html_tag . toggleClass ( "disabled" , value ) ;
return value ;
}
items ( ) : ( top_menu . MenuItem | top_menu . HRItem ) [ ] {
return this . _items ;
}
label ( value? : string ) : string {
if ( typeof ( value ) === "undefined" || this . _label === value )
return this . _label ;
return this . _label ;
}
visible ( value? : boolean ) : boolean {
if ( typeof ( value ) === "undefined" )
return this . html_tag . is ( ':visible' ) ; //FIXME!
this . html_tag . toggle ( ! ! value ) ;
return value ;
}
click ( callback : ( ) = > any ) : this {
this . _callback_click = callback ;
return this ;
}
icon ( klass? : string | Promise < Icon > | Icon ) : string {
this . _label_icon_tag . children ( ) . remove ( ) ;
if ( typeof ( klass ) === "string" )
$ . spawn ( "div" ) . addClass ( "icon_em " + klass ) . appendTo ( this . _label_icon_tag ) ;
else
IconManager . generate_tag ( klass ) . appendTo ( this . _label_icon_tag ) ;
return "" ;
}
}
export class HTMLMenuBarDriver implements MenuBarDriver {
private static _instance : HTMLMenuBarDriver ;
public static instance ( ) : HTMLMenuBarDriver {
if ( ! this . _instance )
this . _instance = new HTMLMenuBarDriver ( ) ;
return this . _instance ;
}
readonly html_tag : JQuery ;
private _items : MenuItem [ ] = [ ] ;
constructor ( ) {
this . html_tag = $ . spawn ( "div" ) . addClass ( "top-menu-bar" ) ;
}
append_item ( label : string ) : top_menu . MenuItem {
const item = new HTMLMenuItem ( label , "down" ) ;
this . _items . push ( item ) ;
this . html_tag . append ( item . html_tag ) ;
2019-10-25 11:04:18 +02:00
item . _label_tag . on ( 'click' , enable_event = > {
enable_event . preventDefault ( ) ;
2019-08-21 10:00:01 +02:00
this . html_tag . find ( ".active" ) . removeClass ( "active" ) ;
item . html_tag . addClass ( "active" ) ;
setTimeout ( ( ) = > {
2019-10-25 11:04:18 +02:00
$ ( document ) . one ( 'click focusout' , event = > {
if ( event . isDefaultPrevented ( ) ) return ;
event . preventDefault ( ) ;
item . html_tag . removeClass ( "active" ) ;
} ) ;
2019-08-21 10:00:01 +02:00
} , 0 ) ;
} ) ;
return item ;
}
delete_item ( item : MenuItem ) {
return undefined ;
}
items ( ) : top_menu . MenuItem [ ] {
return this . _items ;
}
flush_changes() { /* unused, all changed were made instantly */ }
initialize() {
$ ( "#top-menu-bar" ) . replaceWith ( this . html_tag ) ;
}
}
}
let _items_bookmark : {
root : MenuItem ,
manage : MenuItem ,
add_current : MenuItem
} ;
export function rebuild_bookmarks() {
if ( ! _items_bookmark ) {
_items_bookmark = {
root : driver ( ) . append_item ( tr ( "Favorites" ) ) ,
add_current : undefined ,
manage : undefined
} ;
_items_bookmark . manage = _items_bookmark . root . append_item ( tr ( "Manage bookmarks" ) ) ;
_items_bookmark . manage . icon ( "client-bookmark_manager" ) ;
_items_bookmark . manage . click ( ( ) = > Modals . spawnBookmarkModal ( ) ) ;
_items_bookmark . add_current = _items_bookmark . root . append_item ( tr ( "Add current server to bookmarks" ) ) ;
_items_bookmark . add_current . icon ( 'client-bookmark_add' ) ;
_items_bookmark . add_current . click ( ( ) = > bookmarks . add_current_server ( ) ) ;
_state_updater [ "bookmarks.ac" ] = { item : _items_bookmark.add_current , conditions : [ condition_connected ] } ;
}
_items_bookmark . root . items ( ) . filter ( e = > e !== _items_bookmark . add_current && e !== _items_bookmark . manage ) . forEach ( e = > {
_items_bookmark . root . delete_item ( e ) ;
} ) ;
_items_bookmark . root . append_hr ( ) ;
const build_bookmark = ( root : MenuItem , entry : bookmarks.DirectoryBookmark | bookmarks . Bookmark ) = > {
if ( entry . type == bookmarks . BookmarkType . DIRECTORY ) {
const directory = entry as bookmarks . DirectoryBookmark ;
const item = root . append_item ( directory . display_name ) ;
item . icon ( 'client-folder' ) ;
for ( const entry of directory . content )
build_bookmark ( item , entry ) ;
if ( directory . content . length == 0 )
item . disabled ( true ) ;
} else {
const bookmark = entry as bookmarks . Bookmark ;
const item = root . append_item ( bookmark . display_name ) ;
item . icon ( IconManager . load_cached_icon ( bookmark . last_icon_id || 0 ) ) ;
item . click ( ( ) = > bookmarks . boorkmak_connect ( bookmark ) ) ;
}
} ;
for ( const entry of bookmarks . bookmarks ( ) . content )
build_bookmark ( _items_bookmark . root , entry ) ;
driver ( ) . flush_changes ( ) ;
}
/* will be called on connection handler change or on client connect state or mic state change etc... */
let _state_updater : { [ key : string ] : { item : MenuItem ; conditions : ( ( ) = > boolean ) [ ] , update_handler ? : ( item : MenuItem ) = > any } } = { } ;
export function update_state() {
for ( const _key of Object . keys ( _state_updater ) ) {
const item = _state_updater [ _key ] ;
if ( item . update_handler ) {
if ( item . update_handler ( item . item ) )
continue ;
}
let enabled = true ;
for ( const condition of item . conditions )
if ( ! condition ( ) ) {
enabled = false ;
break ;
}
item . item . disabled ( ! enabled ) ;
}
driver ( ) . flush_changes ( ) ;
}
const condition_connected = ( ) = > {
const scon = server_connections ? server_connections . active_connection_handler ( ) : undefined ;
return scon && scon . connected ;
} ;
declare namespace native {
export function initialize ( ) ;
}
export function initialize() {
const driver = top_menu . driver ( ) ;
driver . initialize ( ) ;
/* build connection */
let item : MenuItem ;
{
const menu = driver . append_item ( tr ( "Connection" ) ) ;
2019-10-30 21:50:59 +01:00
item = menu . append_item ( tr ( "Connect to a server" ) ) ;
2019-08-21 10:00:01 +02:00
item . icon ( 'client-connect' ) ;
item . click ( ( ) = > Modals . spawnConnectModal ( { } ) ) ;
const do_disconnect = ( handlers : ConnectionHandler [ ] ) = > {
for ( const handler of handlers ) {
handler . cancel_reconnect ( true ) ;
handler . handleDisconnect ( DisconnectReason . REQUESTED ) ; //TODO message?
server_connections . active_connection_handler ( ) . serverConnection . disconnect ( ) ;
handler . sound . play ( Sound . CONNECTION_DISCONNECTED ) ;
2019-09-12 23:59:35 +02:00
this . log . log ( log . server . Type . DISCONNECTED , { } ) ;
2019-08-21 10:00:01 +02:00
}
control_bar . update_connection_state ( ) ;
update_state ( ) ;
} ;
2019-10-30 21:50:59 +01:00
item = menu . append_item ( tr ( "Disconnect from current server" ) ) ;
2019-08-21 10:00:01 +02:00
item . icon ( 'client-disconnect' ) ;
item . disabled ( true ) ;
item . click ( ( ) = > {
const handler = server_connections . active_connection_handler ( ) ;
do_disconnect ( [ handler ] ) ;
} ) ;
_state_updater [ "connection.dc" ] = { item : item , conditions : [ ( ) = > condition_connected ( ) ] } ;
2019-10-30 21:50:59 +01:00
item = menu . append_item ( tr ( "Disconnect from all servers" ) ) ;
2019-08-21 10:00:01 +02:00
item . icon ( 'client-disconnect' ) ;
item . click ( ( ) = > {
do_disconnect ( server_connections . server_connection_handlers ( ) ) ;
} ) ;
_state_updater [ "connection.dca" ] = { item : item , conditions : [ ] , update_handler : ( item ) = > {
item . visible ( server_connections && server_connections . server_connection_handlers ( ) . length > 1 ) ;
return true ;
} } ;
if ( ! app . is_web ( ) ) {
menu . append_hr ( ) ;
item = menu . append_item ( tr ( "Quit" ) ) ;
item . icon ( 'client-close_button' ) ;
item . click ( ( ) = > native_actions . quit ( ) ) ;
}
}
{
rebuild_bookmarks ( ) ;
}
if ( false ) {
2019-10-30 21:50:59 +01:00
const menu = driver . append_item ( tr ( "Self" ) ) ;
2019-08-21 10:00:01 +02:00
/* Microphone | Sound | Away */
}
{
2019-10-25 11:04:18 +02:00
const menu = driver . append_item ( tr ( "Permissions" ) ) ;
2019-08-21 10:00:01 +02:00
item = menu . append_item ( tr ( "Server Groups" ) ) ;
item . icon ( "client-permission_server_groups" ) ;
item . click ( ( ) = > {
Modals . spawnPermissionEdit ( server_connections . active_connection_handler ( ) , "sg" ) . open ( ) ;
} ) ;
_state_updater [ "permission.sg" ] = { item : item , conditions : [ condition_connected ] } ;
item = menu . append_item ( tr ( "Client Permissions" ) ) ;
item . icon ( "client-permission_client" ) ;
item . click ( ( ) = > {
Modals . spawnPermissionEdit ( server_connections . active_connection_handler ( ) , "clp" ) . open ( ) ;
} ) ;
_state_updater [ "permission.clp" ] = { item : item , conditions : [ condition_connected ] } ;
item = menu . append_item ( tr ( "Channel Client Permissions" ) ) ;
item . icon ( "client-permission_client" ) ;
item . click ( ( ) = > {
Modals . spawnPermissionEdit ( server_connections . active_connection_handler ( ) , "clchp" ) . open ( ) ;
} ) ;
_state_updater [ "permission.chclp" ] = { item : item , conditions : [ condition_connected ] } ;
item = menu . append_item ( tr ( "Channel Groups" ) ) ;
item . icon ( "client-permission_channel" ) ;
item . click ( ( ) = > {
Modals . spawnPermissionEdit ( server_connections . active_connection_handler ( ) , "cg" ) . open ( ) ;
} ) ;
_state_updater [ "permission.cg" ] = { item : item , conditions : [ condition_connected ] } ;
item = menu . append_item ( tr ( "Channel Permissions" ) ) ;
item . icon ( "client-permission_channel" ) ;
item . click ( ( ) = > {
Modals . spawnPermissionEdit ( server_connections . active_connection_handler ( ) , "chp" ) . open ( ) ;
} ) ;
_state_updater [ "permission.cp" ] = { item : item , conditions : [ condition_connected ] } ;
menu . append_hr ( ) ;
item = menu . append_item ( tr ( "List Privilege Keys" ) ) ;
item . icon ( "client-token" ) ;
item . click ( ( ) = > {
createErrorModal ( tr ( "Not implemented" ) , tr ( "Privilege key list is not implemented yet!" ) ) . open ( ) ;
} ) ;
_state_updater [ "permission.pk" ] = { item : item , conditions : [ condition_connected ] } ;
item = menu . append_item ( tr ( "Use Privilege Key" ) ) ;
item . icon ( "client-token_use" ) ;
item . click ( ( ) = > {
//TODO: Fixeme use one method for the control bar and here!
2019-09-19 01:25:57 +02:00
createInputModal ( tr ( "Use token" ) , tr ( "Please enter your token/privilege key" ) , message = > message . length > 0 , result = > {
2019-08-21 10:00:01 +02:00
if ( ! result ) return ;
const scon = server_connections . active_connection_handler ( ) ;
if ( scon . serverConnection . connected )
scon . serverConnection . send_command ( "tokenuse" , {
token : result
} ) . then ( ( ) = > {
createInfoModal ( tr ( "Use token" ) , tr ( "Toke successfully used!" ) ) . open ( ) ;
} ) . catch ( error = > {
//TODO tr
createErrorModal ( tr ( "Use token" ) , MessageHelper . formatMessage ( tr ( "Failed to use token: {}" ) , error instanceof CommandResult ? error.message : error ) ) . open ( ) ;
} ) ;
} ) . open ( ) ;
} ) ;
_state_updater [ "permission.upk" ] = { item : item , conditions : [ condition_connected ] } ;
}
{
2019-10-25 11:04:18 +02:00
const menu = driver . append_item ( tr ( "Tools" ) ) ;
2019-08-21 10:00:01 +02:00
item = menu . append_item ( tr ( "Manage Playlists" ) ) ;
item . icon ( 'client-music' ) ;
item . click ( ( ) = > {
const scon = server_connections . active_connection_handler ( ) ;
if ( scon && scon . connected ) {
Modals . spawnPlaylistManage ( scon ) ;
} else {
createErrorModal ( tr ( "You have to be connected" ) , tr ( "You have to be connected to use this function!" ) ) . open ( ) ;
}
} ) ;
_state_updater [ "tools.pl" ] = { item : item , conditions : [ condition_connected ] } ;
item = menu . append_item ( tr ( "Ban List" ) ) ;
item . icon ( 'client-ban_list' ) ;
item . click ( ( ) = > {
const scon = server_connections . active_connection_handler ( ) ;
if ( scon && scon . connected ) {
if ( scon . permissions . neededPermission ( PermissionType . B_CLIENT_BAN_LIST ) . granted ( 1 ) ) {
Modals . openBanList ( scon ) ;
} else {
createErrorModal ( tr ( "You dont have the permission" ) , tr ( "You dont have the permission to view the ban list" ) ) . open ( ) ;
scon . sound . play ( Sound . ERROR_INSUFFICIENT_PERMISSIONS ) ;
}
} else {
createErrorModal ( tr ( "You have to be connected" ) , tr ( "You have to be connected to use this function!" ) ) . open ( ) ;
}
} ) ;
_state_updater [ "tools.bl" ] = { item : item , conditions : [ condition_connected ] } ;
item = menu . append_item ( tr ( "Query List" ) ) ;
item . icon ( 'client-server_query' ) ;
item . click ( ( ) = > {
const scon = server_connections . active_connection_handler ( ) ;
if ( scon && scon . connected ) {
if ( scon . permissions . neededPermission ( PermissionType . B_CLIENT_QUERY_LIST ) . granted ( 1 ) || scon . permissions . neededPermission ( PermissionType . B_CLIENT_QUERY_LIST_OWN ) . granted ( 1 ) ) {
Modals . spawnQueryManage ( scon ) ;
} else {
createErrorModal ( tr ( "You dont have the permission" ) , tr ( "You dont have the permission to view the server query list" ) ) . open ( ) ;
scon . sound . play ( Sound . ERROR_INSUFFICIENT_PERMISSIONS ) ;
}
} else {
createErrorModal ( tr ( "You have to be connected" ) , tr ( "You have to be connected to use this function!" ) ) . open ( ) ;
}
} ) ;
_state_updater [ "tools.ql" ] = { item : item , conditions : [ condition_connected ] } ;
item = menu . append_item ( tr ( "Query Create" ) ) ;
item . icon ( 'client-server_query' ) ;
item . click ( ( ) = > {
const scon = server_connections . active_connection_handler ( ) ;
if ( scon && scon . connected ) {
2019-10-13 21:33:07 +02:00
if ( scon . permissions . neededPermission ( PermissionType . B_CLIENT_CREATE_MODIFY_SERVERQUERY_LOGIN ) . granted ( 1 ) || scon . permissions . neededPermission ( PermissionType . B_CLIENT_QUERY_CREATE ) . granted ( 1 ) ) {
Modals . spawnQueryCreate ( scon ) ;
2019-08-21 10:00:01 +02:00
} else {
createErrorModal ( tr ( "You dont have the permission" ) , tr ( "You dont have the permission to create a server query login" ) ) . open ( ) ;
scon . sound . play ( Sound . ERROR_INSUFFICIENT_PERMISSIONS ) ;
}
} else {
createErrorModal ( tr ( "You have to be connected" ) , tr ( "You have to be connected to use this function!" ) ) . open ( ) ;
}
} ) ;
_state_updater [ "tools.qc" ] = { item : item , conditions : [ condition_connected ] } ;
menu . append_hr ( ) ;
item = menu . append_item ( tr ( "Settings" ) ) ;
item . icon ( "client-settings" ) ;
item . click ( ( ) = > Modals . spawnSettingsModal ( ) ) ;
}
{
2019-10-25 11:04:18 +02:00
const menu = driver . append_item ( tr ( "Help" ) ) ;
2019-08-21 10:00:01 +02:00
if ( ! app . is_web ( ) ) {
item = menu . append_item ( tr ( "Check for updates" ) ) ;
item . click ( ( ) = > native_actions . check_native_update ( ) ) ;
item = menu . append_item ( tr ( "Open changelog" ) ) ;
item . click ( ( ) = > native_actions . open_change_log ( ) ) ;
}
item = menu . append_item ( tr ( "Visit TeaSpeak.de" ) ) ;
//TODO: Client direct browser?
item . click ( ( ) = > window . open ( 'https://teaspeak.de/' , '_blank' ) ) ;
item = menu . append_item ( tr ( "Visit TeaSpeak forum" ) ) ;
//TODO: Client direct browser?
item . click ( ( ) = > window . open ( 'https://forum.teaspeak.de/' , '_blank' ) ) ;
menu . append_hr ( ) ;
item = menu . append_item ( app . is_web ( ) ? tr ( "About TeaWeb" ) : tr ( "About TeaClient" ) ) ;
item . click ( ( ) = > Modals . spawnAbout ( ) )
}
update_state ( ) ;
}
/* default is HTML, the client will override this */
set_driver ( html . HTMLMenuBarDriver . instance ( ) ) ;
}