2020-09-12 13:49:20 +00:00
import * as log from "../log" ;
import { LogCategory } from "../log" ;
2020-07-20 17:08:13 +00:00
import * as ipc from "../ipc/BrowserIPC" ;
import { ChannelMessage } from "../ipc/BrowserIPC" ;
import * as loader from "tc-loader" ;
import { Stage } from "tc-loader" ;
2020-09-12 13:49:20 +00:00
import { image_type , ImageCache , media_image_type } from "../file/ImageCache" ;
import { FileManager } from "../file/FileManager" ;
2020-06-10 16:13:56 +00:00
import {
FileDownloadTransfer ,
FileTransferState ,
2020-07-12 14:31:57 +00:00
ResponseTransferTarget ,
TransferProvider ,
2020-06-10 16:13:56 +00:00
TransferTargetType
2020-09-12 13:49:20 +00:00
} from "../file/Transfer" ;
import { CommandResult } from "../connection/ServerConnectionDeclaration" ;
import { ClientEntry } from "../tree/Client" ;
import { tr } from "../i18n/localize" ;
2020-07-20 17:08:13 +00:00
import {
AbstractAvatarManager ,
AbstractAvatarManagerFactory ,
AvatarState ,
AvatarStateData ,
ClientAvatar ,
kIPCAvatarChannel ,
setGlobalAvatarManagerFactory ,
uniqueId2AvatarId
2020-09-12 13:49:20 +00:00
} from "../file/Avatars" ;
import { IPCChannel } from "../ipc/BrowserIPC" ;
import { ConnectionHandler } from "../ConnectionHandler" ;
import { ErrorCode } from "../connection/ErrorCode" ;
2020-09-24 09:24:31 +00:00
import { server_connections } from "tc-shared/ConnectionManager" ;
2020-07-12 14:31:57 +00:00
/* FIXME: Retry avatar download after some time! */
2020-05-03 18:59:18 +00:00
2020-07-20 17:08:13 +00:00
class LocalClientAvatar extends ClientAvatar {
protected destroyStateData ( state : AvatarState , data : AvatarStateData [ AvatarState ] ) {
if ( state === "loaded" ) {
const tdata = data as AvatarStateData [ "loaded" ] ;
URL . revokeObjectURL ( tdata . url ) ;
}
2020-07-12 14:31:57 +00:00
}
}
2020-07-20 17:08:13 +00:00
export class AvatarManager extends AbstractAvatarManager {
2020-07-12 14:31:57 +00:00
handle : FileManager ;
private static cache : ImageCache ;
2020-05-03 18:59:18 +00:00
2020-07-20 17:08:13 +00:00
private cachedAvatars : { [ avatarId : string ] : LocalClientAvatar } = { } ;
2020-07-12 14:31:57 +00:00
constructor ( handle : FileManager ) {
2020-07-20 17:08:13 +00:00
super ( ) ;
2020-07-12 14:31:57 +00:00
this . handle = handle ;
2020-05-03 18:59:18 +00:00
2020-07-12 14:31:57 +00:00
if ( ! AvatarManager . cache )
AvatarManager . cache = new ImageCache ( "avatars" ) ;
}
2020-05-03 18:59:18 +00:00
2020-07-12 14:31:57 +00:00
destroy() {
2020-07-20 17:08:13 +00:00
Object . values ( this . cachedAvatars ) . forEach ( e = > e . destroy ( ) ) ;
2020-07-12 14:31:57 +00:00
this . cachedAvatars = { } ;
2020-05-03 18:59:18 +00:00
}
2020-06-10 16:13:56 +00:00
create_avatar_download ( client_avatar_id : string ) : FileDownloadTransfer {
2020-05-03 18:59:18 +00:00
log . debug ( LogCategory . GENERAL , "Requesting download for avatar %s" , client_avatar_id ) ;
2020-06-10 16:13:56 +00:00
return this . handle . initializeFileDownload ( {
path : "" ,
name : "/avatar_" + client_avatar_id ,
targetSupplier : async ( ) = > await TransferProvider . provider ( ) . createResponseTarget ( )
} ) ;
2020-05-03 18:59:18 +00:00
}
2020-07-20 17:08:13 +00:00
private async executeAvatarLoad0 ( avatar : LocalClientAvatar ) {
if ( avatar . getAvatarHash ( ) === "" ) {
avatar . setUnset ( ) ;
2020-07-12 14:31:57 +00:00
return ;
}
2020-07-20 17:08:13 +00:00
let initialAvatarHash = avatar . getAvatarHash ( ) ;
2020-07-12 14:31:57 +00:00
let avatarResponse : Response ;
/* try to lookup our cache for the avatar */
cache_lookup : {
if ( ! AvatarManager . cache . setupped ( ) ) {
await AvatarManager . cache . setup ( ) ;
}
const response = await AvatarManager . cache . resolve_cached ( 'avatar_' + avatar . clientAvatarId ) ; //TODO age!
if ( ! response ) {
break cache_lookup ;
}
let cachedAvatarHash = response . headers . has ( "X-avatar-version" ) ? response . headers . get ( "X-avatar-version" ) : undefined ;
2020-07-20 17:08:13 +00:00
if ( avatar . getAvatarHash ( ) !== "unknown" ) {
2020-07-12 14:31:57 +00:00
if ( cachedAvatarHash === undefined ) {
2020-07-20 17:08:13 +00:00
log . debug ( LogCategory . FILE_TRANSFER , tr ( "Invalidating cached avatar for %s (Version miss match. Cached: unset, Current: %s)" ) , avatar . clientAvatarId , avatar . getAvatarHash ( ) ) ;
2020-07-12 14:31:57 +00:00
await AvatarManager . cache . delete ( 'avatar_' + avatar . clientAvatarId ) ;
break cache_lookup ;
2020-07-20 17:08:13 +00:00
} else if ( cachedAvatarHash !== avatar . getAvatarHash ( ) ) {
log . debug ( LogCategory . FILE_TRANSFER , tr ( "Invalidating cached avatar for %s (Version miss match. Cached: %s, Current: %s)" ) , avatar . clientAvatarId , cachedAvatarHash , avatar . getAvatarHash ( ) ) ;
2020-07-12 14:31:57 +00:00
await AvatarManager . cache . delete ( 'avatar_' + avatar . clientAvatarId ) ;
break cache_lookup ;
}
} else if ( cachedAvatarHash ) {
2020-07-20 17:08:13 +00:00
avatar . events . fire ( "avatar_changed" , { newAvatarHash : cachedAvatarHash } ) ;
initialAvatarHash = cachedAvatarHash ;
2020-07-12 14:31:57 +00:00
}
avatarResponse = response ;
}
/* load the avatar from the server */
if ( ! avatarResponse ) {
let transfer = this . create_avatar_download ( avatar . clientAvatarId ) ;
2020-05-03 18:59:18 +00:00
try {
2020-06-10 16:13:56 +00:00
await transfer . awaitFinished ( ) ;
if ( transfer . transferState ( ) === FileTransferState . CANCELED ) {
throw tr ( "download canceled" ) ;
} else if ( transfer . transferState ( ) === FileTransferState . ERRORED ) {
throw transfer . currentError ( ) ;
2020-07-12 14:31:57 +00:00
} else if ( transfer . transferState ( ) !== FileTransferState . FINISHED ) {
throw tr ( "unknown transfer finished state" ) ;
2020-06-10 16:13:56 +00:00
}
2020-05-03 18:59:18 +00:00
} catch ( error ) {
2020-06-10 16:13:56 +00:00
if ( typeof error === "object" && 'error' in error && error . error === "initialize" ) {
const commandResult = error . commandResult ;
if ( commandResult instanceof CommandResult ) {
2020-08-22 20:23:19 +00:00
if ( commandResult . id === ErrorCode . FILE_NOT_FOUND ) {
2020-07-20 17:08:13 +00:00
if ( avatar . getAvatarHash ( ) !== initialAvatarHash ) {
log . debug ( LogCategory . GENERAL , tr ( "Ignoring avatar not found since the avatar itself got updated. Out version: %s, current version: %s" ) , initialAvatarHash , avatar . getAvatarHash ( ) ) ;
2020-07-12 14:31:57 +00:00
return ;
2020-07-20 17:08:13 +00:00
}
2020-07-12 14:31:57 +00:00
2020-07-20 17:08:13 +00:00
avatar . setUnset ( ) ;
2020-07-12 14:31:57 +00:00
return ;
2020-08-22 20:23:19 +00:00
} else if ( commandResult . id === ErrorCode . SERVER_INSUFFICIENT_PERMISSIONS ) {
2020-07-12 14:31:57 +00:00
throw tr ( "No permissions to download the avatar" ) ;
} else {
2020-06-10 16:13:56 +00:00
throw commandResult . message + ( commandResult . extra_message ? " (" + commandResult . extra_message + ")" : "" ) ;
2020-07-12 14:31:57 +00:00
}
2020-06-10 16:13:56 +00:00
}
}
2020-07-12 14:31:57 +00:00
log . error ( LogCategory . CLIENT , tr ( "Could not request download for avatar %s: %o" ) , avatar . clientAvatarId , error ) ;
2020-06-10 16:13:56 +00:00
if ( error === transfer . currentError ( ) )
throw transfer . currentErrorMessage ( ) ;
2020-07-12 14:31:57 +00:00
2020-06-10 16:13:56 +00:00
throw typeof error === "string" ? error : tr ( "Avatar download failed" ) ;
2020-05-03 18:59:18 +00:00
}
2020-06-10 16:13:56 +00:00
/* could only be tested here, because before we don't know which target we have */
if ( transfer . target . type !== TransferTargetType . RESPONSE )
throw "unsupported transfer target" ;
2020-07-12 14:31:57 +00:00
const transferResponse = transfer . target as ResponseTransferTarget ;
if ( ! transferResponse . hasResponse ( ) ) {
throw tr ( "Avatar transfer has no response" ) ;
}
const headers = transferResponse . getResponse ( ) . headers ;
if ( ! headers . has ( "X-media-bytes" ) ) {
throw tr ( "Avatar response missing media bytes" ) ;
}
2020-06-10 16:13:56 +00:00
2020-07-12 14:31:57 +00:00
const type = image_type ( headers . get ( 'X-media-bytes' ) ) ;
2020-05-03 18:59:18 +00:00
const media = media_image_type ( type ) ;
2020-07-20 17:08:13 +00:00
if ( avatar . getAvatarHash ( ) !== initialAvatarHash ) {
log . debug ( LogCategory . GENERAL , tr ( "Ignoring avatar not found since the avatar itself got updated. Out version: %s, current version: %s" ) , initialAvatarHash , avatar . getAvatarHash ( ) ) ;
2020-07-12 14:31:57 +00:00
return ;
2020-07-20 17:08:13 +00:00
}
2020-07-12 14:31:57 +00:00
await AvatarManager . cache . put_cache ( 'avatar_' + avatar . clientAvatarId , transferResponse . getResponse ( ) . clone ( ) , "image/" + media , {
2020-07-20 17:08:13 +00:00
"X-avatar-version" : avatar . getAvatarHash ( )
2020-05-03 18:59:18 +00:00
} ) ;
2020-07-12 14:31:57 +00:00
avatarResponse = transferResponse . getResponse ( ) ;
2020-05-03 18:59:18 +00:00
}
2020-07-12 14:31:57 +00:00
if ( ! avatarResponse ) {
throw tr ( "Missing avatar response" ) ;
}
/* get an url from the response */
{
if ( ! avatarResponse . headers . has ( 'X-media-bytes' ) )
throw "missing media bytes" ;
const type = image_type ( avatarResponse . headers . get ( 'X-media-bytes' ) ) ;
const media = media_image_type ( type ) ;
2020-05-03 18:59:18 +00:00
2020-07-12 14:31:57 +00:00
const blob = await avatarResponse . blob ( ) ;
/* ensure we're still up to date */
2020-07-20 17:08:13 +00:00
if ( avatar . getAvatarHash ( ) !== initialAvatarHash ) {
log . debug ( LogCategory . GENERAL , tr ( "Ignoring avatar not found since the avatar itself got updated. Out version: %s, current version: %s" ) , initialAvatarHash , avatar . getAvatarHash ( ) ) ;
2020-07-12 14:31:57 +00:00
return ;
2020-07-20 17:08:13 +00:00
}
2020-07-12 14:31:57 +00:00
if ( blob . type !== "image/" + media ) {
2020-07-20 17:08:13 +00:00
avatar . setLoaded ( { url : URL.createObjectURL ( blob . slice ( 0 , blob . size , "image/" + media ) ) } ) ;
2020-07-12 14:31:57 +00:00
} else {
2020-07-20 17:08:13 +00:00
avatar . setLoaded ( { url : URL.createObjectURL ( blob ) } ) ;
2020-07-12 14:31:57 +00:00
}
}
2020-05-03 18:59:18 +00:00
}
2020-07-20 17:08:13 +00:00
private executeAvatarLoad ( avatar : LocalClientAvatar ) {
const avatarHash = avatar . getAvatarHash ( ) ;
2020-07-12 14:31:57 +00:00
2020-07-20 17:08:13 +00:00
avatar . setLoading ( ) ;
2020-07-12 14:31:57 +00:00
avatar . loadingTimestamp = Date . now ( ) ;
this . executeAvatarLoad0 ( avatar ) . catch ( error = > {
2020-07-20 17:08:13 +00:00
if ( avatar . getAvatarHash ( ) !== avatarHash ) {
log . debug ( LogCategory . GENERAL , tr ( "Ignoring avatar not found since the avatar itself got updated. Out version: %s, current version: %s" ) , avatarHash , avatar . getAvatarHash ( ) ) ;
2020-07-12 14:31:57 +00:00
return ;
2020-07-20 17:08:13 +00:00
}
2020-07-12 14:31:57 +00:00
if ( typeof error === "string" ) {
2020-07-20 17:08:13 +00:00
avatar . setErrored ( { message : error } ) ;
2020-07-12 14:31:57 +00:00
} else if ( error instanceof Error ) {
2020-07-20 17:08:13 +00:00
avatar . setErrored ( { message : error.message } ) ;
2020-07-12 14:31:57 +00:00
} else {
2020-07-20 17:08:13 +00:00
log . error ( LogCategory . FILE_TRANSFER , tr ( "Failed to load avatar %s (hash: %s): %o" ) , avatar . clientAvatarId , avatarHash , error ) ;
avatar . setErrored ( { message : tr ( "lookup the console" ) } ) ;
2020-07-12 14:31:57 +00:00
}
} ) ;
}
2020-05-03 18:59:18 +00:00
2020-07-20 17:08:13 +00:00
updateCache ( clientAvatarId : string , clientAvatarHash : string ) {
2020-07-17 21:56:20 +00:00
AvatarManager . cache . setup ( ) . then ( async ( ) = > {
2020-07-12 14:31:57 +00:00
const cached = this . cachedAvatars [ clientAvatarId ] ;
2020-07-17 21:56:20 +00:00
if ( cached ) {
2020-07-20 17:08:13 +00:00
if ( cached . getAvatarHash ( ) === clientAvatarHash )
2020-07-17 21:56:20 +00:00
return ;
2020-07-12 14:31:57 +00:00
2020-07-20 17:08:13 +00:00
log . info ( LogCategory . GENERAL , tr ( "Deleting cached avatar for client %s. Cached version: %s; New version: %s" ) , cached . getAvatarHash ( ) , clientAvatarHash ) ;
2020-07-17 21:56:20 +00:00
}
const response = await AvatarManager . cache . resolve_cached ( 'avatar_' + clientAvatarId ) ;
if ( response ) {
let cachedAvatarHash = response . headers . has ( "X-avatar-version" ) ? response . headers . get ( "X-avatar-version" ) : undefined ;
if ( cachedAvatarHash !== clientAvatarHash ) {
await AvatarManager . cache . delete ( "avatar_" + clientAvatarId ) . catch ( error = > {
log . warn ( LogCategory . FILE_TRANSFER , tr ( "Failed to delete avatar %s: %o" ) , clientAvatarId , error ) ;
} ) ;
}
}
if ( cached ) {
2020-07-20 17:08:13 +00:00
cached . events . fire ( "avatar_changed" , { newAvatarHash : clientAvatarHash } ) ;
2020-07-12 14:31:57 +00:00
this . executeAvatarLoad ( cached ) ;
2020-07-17 21:56:20 +00:00
}
2020-07-12 14:31:57 +00:00
} ) ;
2020-05-03 18:59:18 +00:00
}
2020-07-20 17:08:13 +00:00
resolveAvatar ( clientAvatarId : string , avatarHash? : string , cacheOnly? : boolean ) : ClientAvatar {
2020-07-12 14:31:57 +00:00
let avatar = this . cachedAvatars [ clientAvatarId ] ;
if ( ! avatar ) {
if ( cacheOnly )
return undefined ;
2020-05-03 18:59:18 +00:00
2020-07-20 17:08:13 +00:00
avatar = new LocalClientAvatar ( clientAvatarId ) ;
2020-07-12 14:31:57 +00:00
this . cachedAvatars [ clientAvatarId ] = avatar ;
2020-07-20 17:08:13 +00:00
} else if ( typeof avatarHash !== "string" || avatar . getAvatarHash ( ) === avatarHash ) {
2020-07-12 14:31:57 +00:00
return avatar ;
2020-05-03 18:59:18 +00:00
}
2020-07-20 17:08:13 +00:00
avatar . events . fire ( "avatar_changed" , { newAvatarHash : typeof avatarHash === "string" ? avatarHash : "unknown" } ) ;
2020-07-12 14:31:57 +00:00
this . executeAvatarLoad ( avatar ) ;
return avatar ;
2020-05-03 18:59:18 +00:00
}
2020-07-12 14:31:57 +00:00
resolveClientAvatar ( client : { id? : number , database_id? : number , clientUniqueId : string } ) {
let clientHandle : ClientEntry ;
if ( typeof client . id === "number" ) {
clientHandle = this . handle . connectionHandler . channelTree . findClient ( client . id ) ;
if ( clientHandle ? . properties . client_unique_identifier !== client . clientUniqueId )
clientHandle = undefined ;
2020-05-03 18:59:18 +00:00
}
2020-07-12 14:31:57 +00:00
if ( ! clientHandle && typeof client . database_id === "number" ) {
clientHandle = this . handle . connectionHandler . channelTree . find_client_by_dbid ( client . database_id ) ;
if ( clientHandle ? . properties . client_unique_identifier !== client . clientUniqueId )
clientHandle = undefined ;
2020-05-03 18:59:18 +00:00
}
2020-07-12 14:31:57 +00:00
return this . resolveAvatar ( uniqueId2AvatarId ( client . clientUniqueId ) , clientHandle ? . properties . client_flag_avatar ) ;
2020-05-03 18:59:18 +00:00
}
2020-07-20 17:08:13 +00:00
private static generate_default_image ( ) : JQuery {
2020-05-03 18:59:18 +00:00
return $ . spawn ( "img" ) . attr ( "src" , "img/style/avatar.png" ) . css ( { width : '100%' , height : '100%' } ) ;
}
generate_chat_tag ( client : { id? : number ; database_id? : number ; } , client_unique_id : string , callback_loaded ? : ( successfully : boolean , error? : any ) = > any ) : JQuery {
let client_handle ;
2020-07-12 14:31:57 +00:00
if ( typeof ( client . id ) == "number" ) {
2020-06-10 16:13:56 +00:00
client_handle = this . handle . connectionHandler . channelTree . findClient ( client . id ) ;
2020-07-12 14:31:57 +00:00
}
2020-05-03 18:59:18 +00:00
if ( ! client_handle && typeof ( client . id ) == "number" ) {
2020-06-10 16:13:56 +00:00
client_handle = this . handle . connectionHandler . channelTree . find_client_by_dbid ( client . database_id ) ;
2020-05-03 18:59:18 +00:00
}
2020-07-12 14:31:57 +00:00
if ( client_handle && client_handle . clientUid ( ) !== client_unique_id ) {
2020-05-03 18:59:18 +00:00
client_handle = undefined ;
2020-07-12 14:31:57 +00:00
}
2020-05-03 18:59:18 +00:00
const container = $ . spawn ( "div" ) . addClass ( "avatar" ) ;
if ( client_handle && ! client_handle . properties . client_flag_avatar )
2020-07-20 17:08:13 +00:00
return container . append ( AvatarManager . generate_default_image ( ) ) ;
2020-05-03 18:59:18 +00:00
2020-07-12 14:31:57 +00:00
const clientAvatarId = client_handle ? client_handle . avatarId ( ) : uniqueId2AvatarId ( client_unique_id ) ;
if ( clientAvatarId ) {
const avatar = this . resolveAvatar ( clientAvatarId , client_handle ? . properties . client_flag_avatar ) ;
2020-05-03 18:59:18 +00:00
2020-07-12 14:31:57 +00:00
const updateJQueryTag = ( ) = > {
2020-07-20 17:08:13 +00:00
const image = $ . spawn ( "img" ) . attr ( "src" , avatar . getAvatarUrl ( ) ) . css ( { width : '100%' , height : '100%' } ) ;
2020-07-12 14:31:57 +00:00
container . append ( image ) ;
} ;
2020-05-03 18:59:18 +00:00
2020-07-20 17:08:13 +00:00
if ( avatar . getState ( ) !== "loading" ) {
2020-07-12 14:31:57 +00:00
/* Test if we're may able to load the client avatar sync without a loading screen */
updateJQueryTag ( ) ;
return container ;
}
2020-05-03 18:59:18 +00:00
2020-07-12 14:31:57 +00:00
const image_loading = $ . spawn ( "img" ) . attr ( "src" , "img/loading_image.svg" ) . css ( { width : '100%' , height : '100%' } ) ;
2020-05-03 18:59:18 +00:00
2020-07-12 14:31:57 +00:00
/* lets actually load the avatar */
avatar . awaitLoaded ( ) . then ( updateJQueryTag ) ;
2020-05-03 18:59:18 +00:00
image_loading . appendTo ( container ) ;
} else {
2020-07-20 17:08:13 +00:00
AvatarManager . generate_default_image ( ) . appendTo ( container ) ;
2020-05-03 18:59:18 +00:00
}
return container ;
}
2020-06-10 16:13:56 +00:00
flush_cache() {
2020-07-12 14:31:57 +00:00
this . destroy ( ) ;
2020-06-10 16:13:56 +00:00
}
}
( window as any ) . flush_avatar_cache = async ( ) = > {
server_connections . all_connections ( ) . forEach ( e = > {
e . fileManager . avatars . flush_cache ( ) ;
} ) ;
2020-07-12 14:31:57 +00:00
} ;
2020-07-20 17:08:13 +00:00
/* FIXME: unsubscribe if the other client isn't alive any mnore */
class LocalAvatarManagerFactory extends AbstractAvatarManagerFactory {
private ipcChannel : IPCChannel ;
private subscribedAvatars : { [ key : string ] : { avatar : ClientAvatar , remoteAvatarId : string , unregisterCallback : ( ) = > void } [ ] } = { } ;
constructor ( ) {
super ( ) ;
this . ipcChannel = ipc . getInstance ( ) . createChannel ( undefined , kIPCAvatarChannel ) ;
this . ipcChannel . messageHandler = this . handleIpcMessage . bind ( this ) ;
server_connections . events ( ) . on ( "notify_handler_created" , event = > this . handleHandlerCreated ( event . handler ) ) ;
server_connections . events ( ) . on ( "notify_handler_deleted" , event = > this . handleHandlerDestroyed ( event . handler ) ) ;
}
getManager ( handlerId : string ) : AbstractAvatarManager {
return server_connections . findConnection ( handlerId ) ? . fileManager . avatars ;
}
hasManager ( handlerId : string ) : boolean {
return this . getManager ( handlerId ) !== undefined ;
2020-07-12 14:31:57 +00:00
}
2020-07-20 17:08:13 +00:00
private handleHandlerCreated ( handler : ConnectionHandler ) {
this . ipcChannel . sendMessage ( "notify-handler-created" , { handler : handler.handlerId } ) ;
}
private handleHandlerDestroyed ( handler : ConnectionHandler ) {
this . ipcChannel . sendMessage ( "notify-handler-destroyed" , { handler : handler.handlerId } ) ;
const subscriptions = this . subscribedAvatars [ handler . handlerId ] || [ ] ;
delete this . subscribedAvatars [ handler . handlerId ] ;
subscriptions . forEach ( e = > e . unregisterCallback ( ) ) ;
}
private handleIpcMessage ( remoteId : string , broadcast : boolean , message : ChannelMessage ) {
if ( broadcast )
return ;
if ( message . type === "query-handlers" ) {
this . ipcChannel . sendMessage ( "notify-handlers" , {
handlers : server_connections.all_connections ( ) . map ( e = > e . handlerId )
} , remoteId ) ;
return ;
} else if ( message . type === "load-avatar" ) {
const sendResponse = properties = > {
this . ipcChannel . sendMessage ( "load-avatar-result" , {
avatarId : message.data.avatarId ,
handlerId : message.data.handlerId ,
. . . properties
} , remoteId ) ;
} ;
const avatarId = message . data . avatarId ;
const handlerId = message . data . handlerId ;
const manager = this . getManager ( handlerId ) ;
if ( ! manager ) {
sendResponse ( { success : false , message : tr ( "Invalid handler" ) } ) ;
return ;
}
let avatar : ClientAvatar ;
if ( message . data . keyType === "client" ) {
avatar = manager . resolveClientAvatar ( {
id : message.data.clientId ,
clientUniqueId : message.data.clientUniqueId ,
database_id : message.data.clientDatabaseId
} ) ;
} else {
avatar = manager . resolveAvatar ( message . data . clientAvatarId , message . data . avatarVersion ) ;
}
const subscribedAvatars = this . subscribedAvatars [ handlerId ] || ( this . subscribedAvatars [ handlerId ] = [ ] ) ;
const oldSubscribedAvatarIndex = subscribedAvatars . findIndex ( e = > e . remoteAvatarId === avatarId ) ;
if ( oldSubscribedAvatarIndex !== - 1 ) {
const [ subscription ] = subscribedAvatars . splice ( oldSubscribedAvatarIndex , 1 ) ;
subscription . unregisterCallback ( ) ;
}
subscribedAvatars . push ( {
avatar : avatar ,
remoteAvatarId : avatarId ,
unregisterCallback : avatar.events.onAll ( event = > {
this . ipcChannel . sendMessage ( "avatar-event" , { handlerId : handlerId , avatarId : avatarId , event : event } , remoteId ) ;
} )
} ) ;
sendResponse ( { success : true , state : avatar.getState ( ) , stateData : avatar.getStateData ( ) , hash : avatar.getAvatarHash ( ) } ) ;
2020-07-12 14:31:57 +00:00
}
}
2020-07-20 17:08:13 +00:00
}
loader . register_task ( Stage . LOADED , {
name : "Avatar init" ,
function : async ( ) = > {
setGlobalAvatarManagerFactory ( new LocalAvatarManagerFactory ( ) ) ;
} ,
priority : 5
} ) ;