2020-03-30 11:44:18 +00:00
import * as loader from "tc-loader" ;
2021-02-19 22:05:48 +00:00
import { Stage } from "tc-loader" ;
2020-07-20 17:08:13 +00:00
import * as bipc from "./ipc/BrowserIPC" ;
2020-03-30 11:44:18 +00:00
import * as sound from "./sound/Sounds" ;
import * as i18n from "./i18n/localize" ;
2021-02-19 22:05:48 +00:00
import { tra } from "./i18n/localize" ;
2021-01-10 15:26:45 +00:00
import * as fidentity from "./profiles/identities/TeaForumIdentity" ;
import * as aplayer from "tc-backend/audio/player" ;
import * as ppt from "tc-backend/ppt" ;
import * as global_ev_handler from "./events/ClientGlobalControlHandler" ;
2021-02-19 22:05:48 +00:00
import { AppParameters , settings , Settings , UrlParameterBuilder , UrlParameterParser } from "tc-shared/settings" ;
2021-02-20 15:57:52 +00:00
import { LogCategory , logDebug , logError , logInfo , logWarn } from "tc-shared/log" ;
2020-09-24 13:51:22 +00:00
import { ConnectionHandler } from "tc-shared/ConnectionHandler" ;
2021-02-20 15:57:52 +00:00
import { createErrorModal , createInfoModal } from "tc-shared/ui/elements/Modal" ;
2021-02-19 22:05:48 +00:00
import { RecorderProfile , setDefaultRecorder } from "tc-shared/voice/RecorderProfile" ;
2020-03-30 11:44:18 +00:00
import { spawnYesNo } from "tc-shared/ui/modal/ModalYesNo" ;
import { formatMessage } from "tc-shared/ui/frames/chat" ;
import { openModalNewcomer } from "tc-shared/ui/modal/ModalNewcomer" ;
2020-04-21 14:17:21 +00:00
import { global_client_actions } from "tc-shared/events/GlobalEvents" ;
2020-06-12 09:04:18 +00:00
import { MenuEntryType , spawn_context_menu } from "tc-shared/ui/elements/ContextMenu" ;
2021-01-10 15:13:15 +00:00
import { copyToClipboard } from "tc-shared/utils/helpers" ;
2020-08-23 08:48:15 +00:00
import { checkForUpdatedApp } from "tc-shared/update" ;
import { setupJSRender } from "tc-shared/ui/jsrender" ;
2020-10-05 10:49:13 +00:00
import { ConnectRequestData } from "tc-shared/ipc/ConnectHandler" ;
2021-01-10 15:26:45 +00:00
import { defaultConnectProfile , findConnectProfile } from "tc-shared/profiles/ConnectionProfile" ;
import { server_connections } from "tc-shared/ConnectionManager" ;
import { spawnConnectModalNew } from "tc-shared/ui/modal/connect/Controller" ;
2021-02-19 22:05:48 +00:00
import { initializeKeyControl } from "./KeyControl" ;
import { assertMainApplication } from "tc-shared/ui/utils" ;
2020-04-06 14:28:15 +00:00
2020-03-30 11:44:18 +00:00
/* required import for init */
2021-01-10 15:26:45 +00:00
import "svg-sprites/client-icons" ;
2020-08-05 14:34:39 +00:00
import "../css/load-css"
2020-07-19 16:49:00 +00:00
import "./proto" ;
2020-08-08 13:20:32 +00:00
import "./video-viewer/Controller" ;
2020-09-16 17:30:28 +00:00
import "./profiles/ConnectionProfile" ;
2020-08-22 19:20:25 +00:00
import "./update/UpdaterWeb" ;
2020-09-25 23:22:21 +00:00
import "./file/LocalIcons" ;
2021-01-10 15:26:45 +00:00
import "./connection/CommandHandler" ;
import "./connection/ConnectionBase" ;
2020-11-29 13:42:02 +00:00
import "./connection/rtc/Connection" ;
import "./connection/rtc/video/Connection" ;
import "./video/VideoSource" ;
import "./media/Video" ;
2021-01-05 17:13:57 +00:00
import "./ui/AppController" ;
2021-01-10 15:26:45 +00:00
import "./ui/frames/menu-bar/MainMenu" ;
2021-01-09 13:25:11 +00:00
import "./ui/modal/connect/Controller" ;
2021-01-10 15:26:45 +00:00
import "./ui/elements/ContextDivider" ;
import "./ui/elements/Tab" ;
2021-01-12 02:52:16 +00:00
import "./clientservice" ;
2021-02-20 15:57:52 +00:00
import "./text/bbcode/InviteController" ;
import { clientServiceInvite } from "tc-shared/clientservice" ;
import { ActionResult } from "tc-services" ;
2021-02-26 18:54:07 +00:00
import { CommandResult } from "tc-shared/connection/ServerConnectionDeclaration" ;
import { ErrorCode } from "tc-shared/connection/ErrorCode" ;
2021-01-22 15:50:55 +00:00
assertMainApplication ( ) ;
2021-01-09 13:25:11 +00:00
2020-09-16 18:37:39 +00:00
let preventWelcomeUI = false ;
2020-03-29 10:54:15 +00:00
async function initialize() {
2018-12-15 12:06:41 +00:00
try {
await i18n . initialize ( ) ;
} catch ( error ) {
console . error ( tr ( "Failed to initialized the translation system!\nError: %o" ) , error ) ;
2019-08-30 21:06:39 +00:00
loader . critical_error ( "Failed to setup the translation system" ) ;
2018-12-15 12:06:41 +00:00
return ;
}
2021-02-20 16:46:17 +00:00
bipc . setupIpcHandler ( ) ;
2019-04-04 19:47:52 +00:00
}
2021-01-10 15:26:45 +00:00
async function initializeApp() {
2020-04-09 13:10:14 +00:00
global_ev_handler . initialize ( global_client_actions ) ;
2019-04-04 19:47:52 +00:00
2021-01-05 17:13:57 +00:00
if ( ! aplayer . initialize ( ) ) {
2019-04-04 19:47:52 +00:00
console . warn ( tr ( "Failed to initialize audio controller!" ) ) ;
2021-01-05 17:13:57 +00:00
}
2020-02-09 20:42:49 +00:00
2021-01-10 15:13:15 +00:00
aplayer . on_ready ( ( ) = > aplayer . set_master_volume ( settings . getValue ( Settings . KEY_SOUND_MASTER ) / 100 ) ) ;
2019-04-04 19:47:52 +00:00
2021-02-15 16:47:04 +00:00
const recorder = new RecorderProfile ( "default" ) ;
try {
await recorder . initialize ( ) ;
} catch ( error ) {
/* TODO: Recover into a defined state? */
2021-01-10 15:26:45 +00:00
logError ( LogCategory . AUDIO , tr ( "Failed to initialize default recorder: %o" ) , error ) ;
2021-02-15 16:47:04 +00:00
}
setDefaultRecorder ( recorder ) ;
2019-05-20 16:57:14 +00:00
2019-04-04 19:47:52 +00:00
sound . initialize ( ) . then ( ( ) = > {
2021-01-10 15:26:45 +00:00
logInfo ( LogCategory . AUDIO , tr ( "Sounds initialized" ) ) ;
2019-04-04 19:47:52 +00:00
} ) ;
2021-01-10 15:13:15 +00:00
sound . set_master_volume ( settings . getValue ( Settings . KEY_SOUND_MASTER_SOUNDS ) / 100 ) ;
2019-04-04 19:47:52 +00:00
2018-12-09 19:18:49 +00:00
try {
await ppt . initialize ( ) ;
} catch ( error ) {
2021-01-10 15:26:45 +00:00
logError ( LogCategory . GENERAL , tr ( "Failed to initialize ppt!\nError: %o" ) , error ) ;
2019-08-30 21:06:39 +00:00
loader . critical_error ( tr ( "Failed to initialize ppt!" ) ) ;
2018-12-09 19:18:49 +00:00
return ;
}
}
2021-02-19 22:05:48 +00:00
/* The native client has received a connect request. */
export function handleNativeConnectRequest ( url : URL ) {
let serverAddress = url . host ;
if ( url . searchParams . has ( "port" ) ) {
if ( serverAddress . indexOf ( ':' ) !== - 1 ) {
logWarn ( LogCategory . GENERAL , tr ( "Received connect request which specified the port twice (via parameter and host). Using host port." ) ) ;
} else if ( serverAddress . indexOf ( ":" ) === - 1 ) {
serverAddress += ":" + url . searchParams . get ( "port" ) ;
} else {
serverAddress = ` [ ${ serverAddress } ]: ${ url . searchParams . get ( "port" ) } ` ;
}
}
2019-11-06 13:27:29 +00:00
2021-02-20 15:57:52 +00:00
handleConnectRequest ( serverAddress , undefined , new UrlParameterParser ( url ) ) . then ( undefined ) ;
2021-02-19 22:05:48 +00:00
}
2020-11-28 18:33:54 +00:00
2021-02-20 15:57:52 +00:00
export async function handleConnectRequest ( serverAddress : string , serverUniqueId : string | undefined , parameters : UrlParameterParser ) {
const inviteLinkId = parameters . getValue ( AppParameters . KEY_CONNECT_INVITE_REFERENCE , undefined ) ;
logDebug ( LogCategory . STATISTICS , tr ( "Executing connect request with invite key reference: %o" ) , inviteLinkId ) ;
if ( inviteLinkId ) {
clientServiceInvite . logAction ( inviteLinkId , "ConnectAttempt" ) . then ( result = > {
if ( result . status !== "success" ) {
logWarn ( LogCategory . STATISTICS , tr ( "Failed to register connect attempt: %o" ) , result . result ) ;
}
} ) ;
}
const result = await doHandleConnectRequest ( serverAddress , serverUniqueId , parameters ) ;
if ( inviteLinkId ) {
let promise : Promise < ActionResult < void > > ;
switch ( result . status ) {
case "success" :
promise = clientServiceInvite . logAction ( inviteLinkId , "ConnectSuccess" ) ;
break ;
case "channel-already-joined" :
case "server-already-joined" :
promise = clientServiceInvite . logAction ( inviteLinkId , "ConnectNoAction" , { reason : result.status } ) ;
break ;
default :
promise = clientServiceInvite . logAction ( inviteLinkId , "ConnectFailure" , { reason : result.status } ) ;
break ;
}
promise . then ( result = > {
if ( result . status !== "success" ) {
logWarn ( LogCategory . STATISTICS , tr ( "Failed to register connect result: %o" ) , result . result ) ;
}
} ) ;
}
}
type ConnectRequestResult = {
status :
"success" |
"profile-invalid" |
"client-aborted" |
"server-join-failed" |
"server-already-joined" |
"channel-already-joined" |
"channel-not-visible" |
"channel-join-failed"
}
/ * *
* @param serverAddress The target address to connect to
* @param serverUniqueId If given a server unique id . If any of our current connections matches it , such connection will be used
* @param parameters General connect parameters from the connect URL
* /
async function doHandleConnectRequest ( serverAddress : string , serverUniqueId : string | undefined , parameters : UrlParameterParser ) : Promise < ConnectRequestResult > {
let targetServerConnection : ConnectionHandler ;
let isCurrentServerConnection : boolean ;
if ( serverUniqueId ) {
if ( server_connections . getActiveConnectionHandler ( ) ? . getCurrentServerUniqueId ( ) === serverUniqueId ) {
targetServerConnection = server_connections . getActiveConnectionHandler ( ) ;
isCurrentServerConnection = true ;
} else {
targetServerConnection = server_connections . getAllConnectionHandlers ( ) . find ( connection = > connection . getCurrentServerUniqueId ( ) === serverUniqueId ) ;
isCurrentServerConnection = false ;
}
}
2021-02-19 22:05:48 +00:00
const profileId = parameters . getValue ( AppParameters . KEY_CONNECT_PROFILE , undefined ) ;
2021-02-20 15:57:52 +00:00
const profile = findConnectProfile ( profileId ) || targetServerConnection ? . serverConnection . handshake_handler ( ) ? . parameters . profile || defaultConnectProfile ( ) ;
2021-01-10 15:26:45 +00:00
2021-02-19 22:05:48 +00:00
if ( ! profile || ! profile . valid ( ) ) {
2021-01-10 13:21:38 +00:00
spawnConnectModalNew ( {
2021-02-19 22:05:48 +00:00
selectedAddress : serverAddress ,
2021-01-10 13:21:38 +00:00
selectedProfile : profile
2019-11-06 13:27:29 +00:00
} ) ;
2021-02-20 15:57:52 +00:00
return { status : "profile-invalid" } ;
2019-09-18 23:25:57 +00:00
}
2021-02-19 22:05:48 +00:00
if ( ! aplayer . initialized ( ) ) {
2021-02-20 15:57:52 +00:00
/* Trick the client into clicking somewhere on the site to initialize audio */
const resultPromise = new Promise < boolean > ( resolve = > {
spawnYesNo ( tra ( "Connect to {}" , serverAddress ) , tra ( "Would you like to connect to {}?" , serverAddress ) , resolve ) . open ( ) ;
} ) ;
if ( ! ( await resultPromise ) ) {
/* Well... the client don't want to... */
return { status : "client-aborted" } ;
}
await new Promise ( resolve = > aplayer . on_ready ( resolve ) ) ;
2021-02-19 22:05:48 +00:00
}
const clientNickname = parameters . getValue ( AppParameters . KEY_CONNECT_NICKNAME , undefined ) ;
const serverPassword = parameters . getValue ( AppParameters . KEY_CONNECT_SERVER_PASSWORD , undefined ) ;
const passwordsHashed = parameters . getValue ( AppParameters . KEY_CONNECT_PASSWORDS_HASHED ) ;
const channel = parameters . getValue ( AppParameters . KEY_CONNECT_CHANNEL , undefined ) ;
const channelPassword = parameters . getValue ( AppParameters . KEY_CONNECT_CHANNEL_PASSWORD , undefined ) ;
2021-02-26 18:54:07 +00:00
const connectToken = parameters . getValue ( AppParameters . KEY_CONNECT_TOKEN , undefined ) ;
2021-02-20 15:57:52 +00:00
if ( ! targetServerConnection ) {
targetServerConnection = server_connections . getActiveConnectionHandler ( ) ;
if ( targetServerConnection . connected ) {
targetServerConnection = server_connections . spawnConnectionHandler ( ) ;
}
2021-02-19 22:05:48 +00:00
}
2021-02-20 15:57:52 +00:00
server_connections . setActiveConnectionHandler ( targetServerConnection ) ;
2021-02-20 17:58:09 +00:00
if ( targetServerConnection . getCurrentServerUniqueId ( ) === serverUniqueId ) {
2021-02-20 15:57:52 +00:00
/* Just join the new channel and may use the token (before) */
2021-02-26 18:54:07 +00:00
if ( connectToken ) {
try {
await targetServerConnection . serverConnection . send_command ( "tokenuse" , { token : connectToken } , { process_result : false } ) ;
} catch ( error ) {
if ( error instanceof CommandResult ) {
if ( error . id === ErrorCode . TOKEN_INVALID_ID ) {
targetServerConnection . log . log ( "error.custom" , { message : tr ( "Try to use invite key token but the token is invalid." ) } ) ;
} else if ( error . id == ErrorCode . TOKEN_EXPIRED ) {
targetServerConnection . log . log ( "error.custom" , { message : tr ( "Try to use invite key token but the token is expired." ) } ) ;
} else if ( error . id === ErrorCode . TOKEN_USE_LIMIT_EXCEEDED ) {
targetServerConnection . log . log ( "error.custom" , { message : tr ( "Try to use invite key token but the token has been used too many times." ) } ) ;
} else {
targetServerConnection . log . log ( "error.custom" , { message : tra ( "Try to use invite key token but an error occurred: {}" , error . formattedMessage ( ) ) } ) ;
}
} else {
logError ( LogCategory . GENERAL , tr ( "Failed to use token: {}" ) , error ) ;
}
}
}
2021-02-20 15:57:52 +00:00
if ( ! channel ) {
/* No need to join any channel */
2021-02-26 18:54:07 +00:00
if ( ! connectToken ) {
2021-02-20 15:57:52 +00:00
createInfoModal ( tr ( "Already connected" ) , tr ( "You're already connected to the target server." ) ) . open ( ) ;
} else {
/* Don't show a message since a token has been used */
}
return { status : "server-already-joined" } ;
}
const targetChannel = targetServerConnection . channelTree . resolveChannelPath ( channel ) ;
if ( ! targetChannel ) {
createErrorModal ( tr ( "Missing target channel" ) , tr ( "Failed to join channel since it is not visible." ) ) . open ( ) ;
return { status : "channel-not-visible" } ;
}
if ( targetServerConnection . getClient ( ) . currentChannel ( ) === targetChannel ) {
createErrorModal ( tr ( "Channel already joined" ) , tr ( "You already joined the channel." ) ) . open ( ) ;
return { status : "channel-already-joined" } ;
}
if ( targetChannel . getCachedPasswordHash ( ) ) {
const succeeded = await targetChannel . joinChannel ( ) ;
if ( succeeded ) {
/* Successfully joined channel with a password we already knew */
return { status : "success" } ;
}
}
targetChannel . setCachedHashedPassword ( channelPassword ) ;
2021-02-26 18:54:07 +00:00
/* Force join the channel. Either we have the password, can ignore the password or we don't want to join. */
if ( await targetChannel . joinChannel ( true ) ) {
2021-02-20 15:57:52 +00:00
return { status : "success" } ;
} else {
/* TODO: More detail? */
return { status : "channel-join-failed" } ;
}
} else {
await targetServerConnection . startConnectionNew ( {
targetAddress : serverAddress ,
nickname : clientNickname ,
nicknameSpecified : false ,
2021-02-19 22:05:48 +00:00
2021-02-20 15:57:52 +00:00
profile : profile ,
2021-02-26 18:54:07 +00:00
token : connectToken ,
2021-02-19 22:05:48 +00:00
2021-02-20 15:57:52 +00:00
serverPassword : serverPassword ,
serverPasswordHashed : passwordsHashed ,
2021-02-19 22:05:48 +00:00
2021-02-20 15:57:52 +00:00
defaultChannel : channel ,
defaultChannelPassword : channelPassword ,
defaultChannelPasswordHashed : passwordsHashed
} , false ) ;
2021-02-19 22:05:48 +00:00
2021-02-20 15:57:52 +00:00
if ( targetServerConnection . connected ) {
return { status : "success" } ;
} else {
/* TODO: More detail? */
return { status : "server-join-failed" } ;
}
}
2021-02-19 22:05:48 +00:00
}
/* Used by the old native clients (an within the multi instance handler). Delete it later */
export function handle_connect_request ( properties : ConnectRequestData , _connection : ConnectionHandler ) {
const urlBuilder = new UrlParameterBuilder ( ) ;
urlBuilder . setValue ( AppParameters . KEY_CONNECT_PROFILE , properties . profile ) ;
urlBuilder . setValue ( AppParameters . KEY_CONNECT_NICKNAME , properties . username ) ;
urlBuilder . setValue ( AppParameters . KEY_CONNECT_SERVER_PASSWORD , properties . password ? . value ) ;
urlBuilder . setValue ( AppParameters . KEY_CONNECT_PASSWORDS_HASHED , properties . password ? . hashed ) ;
const url = new URL ( ` https://localhost/? ${ urlBuilder . build ( ) } ` ) ;
2021-02-20 15:57:52 +00:00
handleConnectRequest ( properties . address , undefined , new UrlParameterParser ( url ) ) ;
2019-09-18 23:25:57 +00:00
}
2018-12-09 19:18:49 +00:00
function main() {
2019-08-21 08:00:01 +00:00
/* initialize font */
{
2021-01-10 15:13:15 +00:00
const font = settings . getValue ( Settings . KEY_FONT_SIZE ) ;
2021-01-10 15:26:45 +00:00
document . body . style . fontSize = font + "px" ;
2020-09-17 21:06:02 +00:00
settings . globalChangeListener ( Settings . KEY_FONT_SIZE , value = > {
2021-01-10 15:26:45 +00:00
document . body . style . fontSize = value + "px" ;
} ) ;
2019-08-21 08:00:01 +00:00
}
/* context menu prevent */
2021-01-10 15:26:45 +00:00
document . addEventListener ( "contextmenu" , event = > {
if ( event . defaultPrevented ) {
2019-08-21 08:00:01 +00:00
return ;
2020-09-17 21:19:30 +00:00
}
2019-08-21 08:00:01 +00:00
2020-06-12 09:04:18 +00:00
if ( event . target instanceof HTMLInputElement ) {
2021-01-10 15:26:45 +00:00
const target = event . target ;
2020-06-12 09:36:47 +00:00
if ( ( ! ! event . target . value || __build . target === "client" ) && ! event . target . disabled && ! event . target . readOnly && event . target . type !== "number" ) {
2020-06-12 09:04:18 +00:00
spawn_context_menu ( event . pageX , event . pageY , {
type : MenuEntryType . ENTRY ,
name : tr ( "Copy" ) ,
2021-01-10 15:26:45 +00:00
callback : ( ) = > copyToClipboard ( target . value ) ,
2020-06-12 09:04:18 +00:00
icon_class : "client-copy" ,
visible : ! ! event . target . value
} , {
type : MenuEntryType . ENTRY ,
name : tr ( "Paste" ) ,
callback : ( ) = > {
const { clipboard } = __non_webpack_require__ ( 'electron' ) ;
2021-01-10 15:26:45 +00:00
target . value = clipboard . readText ( ) ;
2020-06-12 09:04:18 +00:00
} ,
icon_class : "client-copy" ,
visible : __build.target === "client" ,
} ) ;
}
2021-01-10 15:13:15 +00:00
2020-06-12 09:04:18 +00:00
event . preventDefault ( ) ;
return ;
}
2021-01-10 15:13:15 +00:00
if ( settings . getValue ( Settings . KEY_DISABLE_GLOBAL_CONTEXT_MENU ) ) {
2019-08-21 08:00:01 +00:00
event . preventDefault ( ) ;
2020-09-17 21:19:30 +00:00
}
2019-08-21 08:00:01 +00:00
} ) ;
2020-09-17 21:19:30 +00:00
window . removeLoaderContextMenuHook ( ) ;
2019-08-21 08:00:01 +00:00
2021-01-10 15:26:45 +00:00
const initialHandler = server_connections . spawnConnectionHandler ( ) ;
server_connections . setActiveConnectionHandler ( initialHandler ) ;
2021-01-10 15:13:15 +00:00
initialHandler . acquireInputHardware ( ) . then ( ( ) = > { } ) ;
2020-09-24 13:51:22 +00:00
2018-04-11 15:56:09 +00:00
/** Setup the XF forum identity **/
2020-03-30 11:44:18 +00:00
fidentity . update_forum ( ) ;
2021-01-10 15:26:45 +00:00
initializeKeyControl ( ) ;
2019-02-25 14:59:42 +00:00
2020-08-22 19:20:25 +00:00
checkForUpdatedApp ( ) ;
2019-09-18 23:25:57 +00:00
2021-01-10 15:13:15 +00:00
if ( settings . getValue ( Settings . KEY_USER_IS_NEW ) && ! preventWelcomeUI ) {
2020-09-16 18:37:39 +00:00
const modal = openModalNewcomer ( ) ;
2021-01-10 15:13:15 +00:00
modal . close_listener . push ( ( ) = > settings . setValue ( Settings . KEY_USER_IS_NEW , false ) ) ;
2020-03-27 15:15:15 +00:00
}
2018-04-19 16:42:34 +00:00
}
2019-04-04 19:47:52 +00:00
const task_teaweb_starter : loader.Task = {
name : "voice app starter" ,
2018-12-30 00:38:13 +00:00
function : async ( ) = > {
try {
2021-01-10 15:26:45 +00:00
await initializeApp ( ) ;
2018-12-30 00:38:13 +00:00
main ( ) ;
2020-03-30 11:44:18 +00:00
if ( ! aplayer . initialized ( ) ) {
2021-01-10 15:26:45 +00:00
logInfo ( LogCategory . VOICE , tr ( "Initialize audio controller later!" ) ) ;
2020-03-30 11:44:18 +00:00
if ( ! aplayer . initializeFromGesture ) {
console . error ( tr ( "Missing aplayer.initializeFromGesture" ) ) ;
2021-01-10 15:13:15 +00:00
} else {
2020-08-13 11:05:37 +00:00
$ ( document ) . one ( 'click' , ( ) = > aplayer . initializeFromGesture ( ) ) ;
2021-01-10 15:13:15 +00:00
}
2018-12-30 00:38:13 +00:00
}
2021-01-10 15:13:15 +00:00
loader . config . abortAnimationOnFinish = settings . getValue ( Settings . KEY_LOADER_ANIMATION_ABORT ) ;
2018-12-30 00:38:13 +00:00
} catch ( ex ) {
console . error ( ex . stack ) ;
2021-01-10 15:13:15 +00:00
if ( ex instanceof ReferenceError || ex instanceof TypeError ) {
2018-12-30 00:38:13 +00:00
ex = ex . name + ": " + ex . message ;
2021-01-10 15:13:15 +00:00
}
2019-08-30 21:06:39 +00:00
loader . critical_error ( "Failed to invoke main function:<br>" + ex ) ;
2018-09-25 15:39:38 +00:00
}
2018-12-30 00:38:13 +00:00
} ,
priority : 10
2019-04-04 19:47:52 +00:00
} ;
2019-11-06 13:27:29 +00:00
const task_connect_handler : loader.Task = {
name : "Connect handler" ,
function : async ( ) = > {
2021-01-10 15:13:15 +00:00
const address = AppParameters . getValue ( AppParameters . KEY_CONNECT_ADDRESS , undefined ) ;
if ( typeof address === "undefined" ) {
loader . register_task ( loader . Stage . LOADED , task_teaweb_starter ) ;
return ;
}
2019-11-06 13:27:29 +00:00
2021-02-19 22:05:48 +00:00
/* FIXME: All additional connect parameters! */
2021-01-10 15:13:15 +00:00
const connectData = {
address : address ,
2019-11-06 13:27:29 +00:00
2021-01-10 15:13:15 +00:00
profile : AppParameters.getValue ( AppParameters . KEY_CONNECT_PROFILE , "" ) ,
username : AppParameters.getValue ( AppParameters . KEY_CONNECT_NICKNAME , "" ) ,
password : {
value : AppParameters.getValue ( AppParameters . KEY_CONNECT_SERVER_PASSWORD , "" ) ,
hashed : true
2019-11-06 13:27:29 +00:00
}
2021-01-10 15:13:15 +00:00
} ;
2019-11-06 13:27:29 +00:00
2021-01-10 15:13:15 +00:00
const chandler = bipc . getInstanceConnectHandler ( ) ;
if ( chandler && AppParameters . getValue ( AppParameters . KEY_CONNECT_NO_SINGLE_INSTANCE ) ) {
try {
await chandler . post_connect_request ( connectData , ( ) = > new Promise < boolean > ( resolve = > {
spawnYesNo ( tr ( "Another TeaWeb instance is already running" ) , tra ( "Another TeaWeb instance is already running.{:br:}Would you like to connect there?" ) , response = > {
resolve ( response ) ;
} , {
closeable : false
} ) . open ( ) ;
} ) ) ;
2021-01-10 15:26:45 +00:00
logInfo ( LogCategory . CLIENT , tr ( "Executed connect successfully in another browser window. Closing this window" ) ) ;
2021-01-10 15:13:15 +00:00
const message =
"You're connecting to {0} within the other TeaWeb instance.{:br:}" +
"You could now close this page." ;
createInfoModal (
tr ( "Connecting successfully within other instance" ) ,
formatMessage ( /* @tr-ignore */ tr ( message ) , connectData . address ) ,
{
closeable : false ,
footer : undefined
}
) . open ( ) ;
return ;
} catch ( error ) {
2021-01-10 15:26:45 +00:00
logInfo ( LogCategory . CLIENT , tr ( "Failed to execute connect within other TeaWeb instance. Using this one. Error: %o" ) , error ) ;
2021-01-10 15:13:15 +00:00
}
if ( chandler ) {
/* no instance avail, so lets make us avail */
chandler . callback_available = ( ) = > {
return ! settings . getValue ( Settings . KEY_DISABLE_MULTI_SESSION ) ;
} ;
chandler . callback_execute = data = > {
preventWelcomeUI = true ;
2021-01-10 15:26:45 +00:00
handle_connect_request ( data , server_connections . spawnConnectionHandler ( ) ) ;
2021-01-10 15:13:15 +00:00
return true ;
}
2019-11-06 13:27:29 +00:00
}
}
2021-01-10 15:13:15 +00:00
preventWelcomeUI = true ;
loader . register_task ( loader . Stage . LOADED , {
priority : 0 ,
2021-02-20 15:57:52 +00:00
function : async ( ) = > handleConnectRequest ( address , undefined , AppParameters . Instance ) ,
2021-01-10 15:13:15 +00:00
name : tr ( "default url connect" )
} ) ;
loader . register_task ( loader . Stage . LOADED , task_teaweb_starter ) ;
2019-04-04 19:47:52 +00:00
} ,
priority : 10
} ;
2019-04-25 18:22:13 +00:00
loader . register_task ( loader . Stage . JAVASCRIPT_INITIALIZING , {
name : "jrendere initialize" ,
function : async ( ) = > {
try {
2020-08-23 08:48:15 +00:00
if ( ! setupJSRender ( ) )
2019-04-25 18:22:13 +00:00
throw "invalid load" ;
} catch ( error ) {
2019-08-30 21:06:39 +00:00
loader . critical_error ( tr ( "Failed to setup jsrender" ) ) ;
2019-04-25 18:22:13 +00:00
console . error ( tr ( "Failed to load jsrender! %o" ) , error ) ;
return ;
}
} ,
2020-09-24 13:51:22 +00:00
priority : 110
2019-05-20 17:28:20 +00:00
} ) ;
2019-04-25 18:22:13 +00:00
2019-05-20 17:28:20 +00:00
loader . register_task ( loader . Stage . JAVASCRIPT_INITIALIZING , {
2019-04-04 19:47:52 +00:00
name : "app starter" ,
function : async ( ) = > {
try {
await initialize ( ) ;
2020-04-01 19:47:33 +00:00
if ( __build . target == "web" ) {
2021-01-10 15:13:15 +00:00
loader . register_task ( loader . Stage . LOADED , task_connect_handler ) ;
2019-11-06 13:27:29 +00:00
} else {
2019-04-04 19:47:52 +00:00
loader . register_task ( loader . Stage . LOADED , task_teaweb_starter ) ;
2019-11-06 13:27:29 +00:00
}
2019-04-04 19:47:52 +00:00
} catch ( ex ) {
2021-01-10 15:13:15 +00:00
if ( ex instanceof Error || typeof ( ex . stack ) !== "undefined" ) {
2019-05-21 17:11:53 +00:00
console . error ( ( tr || ( msg = > msg ) ) ( "Critical error stack trace: %o" ) , ex . stack ) ;
2021-01-10 15:13:15 +00:00
}
2019-05-21 17:11:53 +00:00
2021-01-10 15:13:15 +00:00
if ( ex instanceof ReferenceError || ex instanceof TypeError ) {
2019-04-04 19:47:52 +00:00
ex = ex . name + ": " + ex . message ;
2021-01-10 15:13:15 +00:00
}
2019-08-30 21:06:39 +00:00
loader . critical_error ( "Failed to boot app function:<br>" + ex ) ;
2019-04-04 19:47:52 +00:00
}
} ,
2019-05-24 20:30:58 +00:00
priority : 1000
2018-10-06 13:13:45 +00:00
} ) ;
2018-12-30 00:38:13 +00:00
2020-03-30 11:44:18 +00:00
loader . register_task ( loader . Stage . LOADED , {
name : "error task" ,
function : async ( ) = > {
2021-01-10 15:13:15 +00:00
if ( AppParameters . getValue ( AppParameters . KEY_LOAD_DUMMY_ERROR ) ) {
loader . critical_error ( "The tea is cold!" , "Argh, this is evil! Cold tea does not taste good." ) ;
2020-03-30 11:44:18 +00:00
throw "The tea is cold!" ;
}
} ,
2021-01-10 15:13:15 +00:00
priority : 2000
2020-09-24 13:51:22 +00:00
} ) ;
2021-01-10 15:26:45 +00:00
/* TODO: Remove this after the image preview has been rewritten into react */
2020-09-24 13:51:22 +00:00
loader . register_task ( Stage . JAVASCRIPT_INITIALIZING , {
name : "app init" ,
function : async ( ) = > {
2021-01-10 15:26:45 +00:00
try {
$ ( "body" ) . append ( $ ( "#tmpl_main" ) . renderTag ( ) ) ;
2020-09-24 13:51:22 +00:00
} catch ( error ) {
2021-01-10 15:26:45 +00:00
logError ( LogCategory . GENERAL , error ) ;
2020-09-24 13:51:22 +00:00
loader . critical_error ( tr ( "Failed to setup main page!" ) ) ;
return ;
}
} ,
priority : 100
2020-04-01 13:40:45 +00:00
} ) ;