Finalizing the 1.5.3 client
This commit is contained in:
parent
145f1ff0b1
commit
4a19f3a827
2
github
2
github
|
@ -1 +1 @@
|
|||
Subproject commit 35052680fe58de01102dfd4b65835df08c2a7f20
|
||||
Subproject commit 90d3a6c84c003ab42a3ecfb5197b9368897862b1
|
|
@ -45,9 +45,9 @@ export declare class BookmarkManager {
|
|||
private readonly registeredBookmarks;
|
||||
private defaultBookmarkCreated;
|
||||
constructor();
|
||||
private loadBookmarks;
|
||||
loadBookmarks(): Promise<void>;
|
||||
private importOldBookmarks;
|
||||
private saveBookmarks;
|
||||
saveBookmarks(): Promise<void>;
|
||||
getRegisteredBookmarks(): BookmarkEntry[];
|
||||
getOrderedRegisteredBookmarks(): OrderedBookmarkEntry[];
|
||||
findBookmark(uniqueId: string): BookmarkEntry | undefined;
|
||||
|
|
|
@ -104,6 +104,7 @@ export declare class ConnectionHandler {
|
|||
settings: ServerSettings;
|
||||
sound: SoundManager;
|
||||
serverFeatures: ServerFeatures;
|
||||
log: ServerEventLog;
|
||||
private sideBar;
|
||||
private playlistManager;
|
||||
private channelConversations;
|
||||
|
@ -116,13 +117,12 @@ export declare class ConnectionHandler {
|
|||
private connectAttemptId;
|
||||
private echoTestRunning;
|
||||
private pluginCmdRegistry;
|
||||
private client_status;
|
||||
private handlerState;
|
||||
private clientStatusSync;
|
||||
private inputHardwareState;
|
||||
private listenerRecorderInputDeviceChanged;
|
||||
log: ServerEventLog;
|
||||
constructor();
|
||||
initialize_client_state(source?: ConnectionHandler): void;
|
||||
initializeHandlerState(source?: ConnectionHandler): void;
|
||||
events(): Registry<ConnectionEvents>;
|
||||
startConnectionNew(parameters: ConnectParameters, autoReconnectAttempt: boolean): Promise<void>;
|
||||
startConnection(addr: string, profile: ConnectionProfile, user_action: boolean, parameters: ConnectParametersOld): Promise<void>;
|
||||
|
@ -142,7 +142,6 @@ export declare class ConnectionHandler {
|
|||
private _certificate_modal;
|
||||
handleDisconnect(type: DisconnectReason, data?: any): void;
|
||||
cancelAutoReconnect(log_event: boolean): void;
|
||||
private on_connection_state_changed;
|
||||
private updateVoiceStatus;
|
||||
private lastRecordErrorPopup;
|
||||
update_voice_status(): void;
|
||||
|
@ -156,7 +155,6 @@ export declare class ConnectionHandler {
|
|||
}>;
|
||||
getVoiceRecorder(): RecorderProfile | undefined;
|
||||
reconnect_properties(profile?: ConnectionProfile): ConnectParametersOld;
|
||||
update_avatar(): void;
|
||||
private initializeWhisperSession;
|
||||
destroy(): void;
|
||||
setMicrophoneMuted(muted: boolean, dontPlaySound?: boolean): void;
|
||||
|
@ -192,8 +190,5 @@ export interface ConnectionEvents {
|
|||
oldState: ConnectionState;
|
||||
newState: ConnectionState;
|
||||
};
|
||||
notify_visibility_changed: {
|
||||
visible: boolean;
|
||||
};
|
||||
notify_handler_initialized: {};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
export declare class Mutex<T> {
|
||||
private value;
|
||||
private taskExecuting;
|
||||
private taskQueue;
|
||||
private freeListener;
|
||||
constructor(value: T);
|
||||
isFree(): boolean;
|
||||
awaitFree(): Promise<void>;
|
||||
execute<R>(callback: (value: T, setValue: (newValue: T) => void) => R | Promise<R>): Promise<R>;
|
||||
tryExecute<R>(callback: (value: T, setValue: (newValue: T) => void) => R | Promise<R>): Promise<{
|
||||
status: "success";
|
||||
result: R;
|
||||
} | {
|
||||
status: "would-block";
|
||||
}>;
|
||||
private executeNextTask;
|
||||
private triggerFinished;
|
||||
}
|
|
@ -1,122 +1,3 @@
|
|||
export declare enum KeyCode {
|
||||
KEY_CANCEL = 3,
|
||||
KEY_HELP = 6,
|
||||
KEY_BACK_SPACE = 8,
|
||||
KEY_TAB = 9,
|
||||
KEY_CLEAR = 12,
|
||||
KEY_RETURN = 13,
|
||||
KEY_ENTER = 14,
|
||||
KEY_SHIFT = 16,
|
||||
KEY_CONTROL = 17,
|
||||
KEY_ALT = 18,
|
||||
KEY_PAUSE = 19,
|
||||
KEY_CAPS_LOCK = 20,
|
||||
KEY_ESCAPE = 27,
|
||||
KEY_SPACE = 32,
|
||||
KEY_PAGE_UP = 33,
|
||||
KEY_PAGE_DOWN = 34,
|
||||
KEY_END = 35,
|
||||
KEY_HOME = 36,
|
||||
KEY_LEFT = 37,
|
||||
KEY_UP = 38,
|
||||
KEY_RIGHT = 39,
|
||||
KEY_DOWN = 40,
|
||||
KEY_PRINTSCREEN = 44,
|
||||
KEY_INSERT = 45,
|
||||
KEY_DELETE = 46,
|
||||
KEY_0 = 48,
|
||||
KEY_1 = 49,
|
||||
KEY_2 = 50,
|
||||
KEY_3 = 51,
|
||||
KEY_4 = 52,
|
||||
KEY_5 = 53,
|
||||
KEY_6 = 54,
|
||||
KEY_7 = 55,
|
||||
KEY_8 = 56,
|
||||
KEY_9 = 57,
|
||||
KEY_SEMICOLON = 59,
|
||||
KEY_EQUALS = 61,
|
||||
KEY_A = 65,
|
||||
KEY_B = 66,
|
||||
KEY_C = 67,
|
||||
KEY_D = 68,
|
||||
KEY_E = 69,
|
||||
KEY_F = 70,
|
||||
KEY_G = 71,
|
||||
KEY_H = 72,
|
||||
KEY_I = 73,
|
||||
KEY_J = 74,
|
||||
KEY_K = 75,
|
||||
KEY_L = 76,
|
||||
KEY_M = 77,
|
||||
KEY_N = 78,
|
||||
KEY_O = 79,
|
||||
KEY_P = 80,
|
||||
KEY_Q = 81,
|
||||
KEY_R = 82,
|
||||
KEY_S = 83,
|
||||
KEY_T = 84,
|
||||
KEY_U = 85,
|
||||
KEY_V = 86,
|
||||
KEY_W = 87,
|
||||
KEY_X = 88,
|
||||
KEY_Y = 89,
|
||||
KEY_Z = 90,
|
||||
KEY_LEFT_CMD = 91,
|
||||
KEY_RIGHT_CMD = 93,
|
||||
KEY_CONTEXT_MENU = 93,
|
||||
KEY_NUMPAD0 = 96,
|
||||
KEY_NUMPAD1 = 97,
|
||||
KEY_NUMPAD2 = 98,
|
||||
KEY_NUMPAD3 = 99,
|
||||
KEY_NUMPAD4 = 100,
|
||||
KEY_NUMPAD5 = 101,
|
||||
KEY_NUMPAD6 = 102,
|
||||
KEY_NUMPAD7 = 103,
|
||||
KEY_NUMPAD8 = 104,
|
||||
KEY_NUMPAD9 = 105,
|
||||
KEY_MULTIPLY = 106,
|
||||
KEY_ADD = 107,
|
||||
KEY_SEPARATOR = 108,
|
||||
KEY_SUBTRACT = 109,
|
||||
KEY_DECIMAL = 110,
|
||||
KEY_DIVIDE = 111,
|
||||
KEY_F1 = 112,
|
||||
KEY_F2 = 113,
|
||||
KEY_F3 = 114,
|
||||
KEY_F4 = 115,
|
||||
KEY_F5 = 116,
|
||||
KEY_F6 = 117,
|
||||
KEY_F7 = 118,
|
||||
KEY_F8 = 119,
|
||||
KEY_F9 = 120,
|
||||
KEY_F10 = 121,
|
||||
KEY_F11 = 122,
|
||||
KEY_F12 = 123,
|
||||
KEY_F13 = 124,
|
||||
KEY_F14 = 125,
|
||||
KEY_F15 = 126,
|
||||
KEY_F16 = 127,
|
||||
KEY_F17 = 128,
|
||||
KEY_F18 = 129,
|
||||
KEY_F19 = 130,
|
||||
KEY_F20 = 131,
|
||||
KEY_F21 = 132,
|
||||
KEY_F22 = 133,
|
||||
KEY_F23 = 134,
|
||||
KEY_F24 = 135,
|
||||
KEY_NUM_LOCK = 144,
|
||||
KEY_SCROLL_LOCK = 145,
|
||||
KEY_COMMA = 188,
|
||||
KEY_PERIOD = 190,
|
||||
KEY_SLASH = 191,
|
||||
KEY_BACK_QUOTE = 192,
|
||||
KEY_OPEN_BRACKET = 219,
|
||||
KEY_BACK_SLASH = 220,
|
||||
KEY_CLOSE_BRACKET = 221,
|
||||
KEY_QUOTE = 222,
|
||||
KEY_META = 224
|
||||
}
|
||||
export declare enum EventType {
|
||||
KEY_PRESS = 0,
|
||||
KEY_RELEASE = 1,
|
||||
|
@ -139,14 +20,17 @@ export interface KeyEvent extends KeyDescriptor {
|
|||
readonly type: EventType;
|
||||
readonly key: string;
|
||||
}
|
||||
export interface KeyHook extends KeyDescriptor {
|
||||
export interface KeyHook extends Partial<KeyDescriptor> {
|
||||
callbackPress: () => any;
|
||||
callbackRelease: () => any;
|
||||
}
|
||||
interface RegisteredKeyHook extends KeyHook {
|
||||
triggered: boolean;
|
||||
}
|
||||
export interface KeyBoardBackend {
|
||||
registerListener(listener: (event: KeyEvent) => void): any;
|
||||
unregisterListener(listener: (event: KeyEvent) => void): any;
|
||||
registerHook(hook: KeyHook): any;
|
||||
registerHook(hook: KeyHook): () => void;
|
||||
unregisterHook(hook: KeyHook): any;
|
||||
isKeyPressed(key: string | SpecialKey): boolean;
|
||||
}
|
||||
|
@ -156,18 +40,19 @@ export declare class AbstractKeyBoard implements KeyBoardBackend {
|
|||
[key: number]: boolean;
|
||||
};
|
||||
protected readonly activeKeys: any;
|
||||
protected registeredKeyHooks: KeyHook[];
|
||||
protected activeKeyHooks: KeyHook[];
|
||||
protected registeredKeyHooks: RegisteredKeyHook[];
|
||||
constructor();
|
||||
protected destroy(): void;
|
||||
isKeyPressed(key: string | SpecialKey): boolean;
|
||||
registerHook(hook: KeyHook): void;
|
||||
registerHook(hook: KeyHook): () => void;
|
||||
unregisterHook(hook: KeyHook): void;
|
||||
registerListener(listener: (event: KeyEvent) => void): void;
|
||||
unregisterListener(listener: (event: KeyEvent) => void): void;
|
||||
private shouldHookBeActive;
|
||||
protected fireKeyEvent(event: KeyEvent): void;
|
||||
protected resetKeyboardState(): void;
|
||||
}
|
||||
export declare function getKeyBoard(): KeyBoardBackend;
|
||||
export declare function setKeyBoardBackend(newBackend: KeyBoardBackend): void;
|
||||
export declare function getKeyDescription(key: KeyDescriptor): string;
|
||||
export {};
|
||||
|
|
|
@ -23,6 +23,7 @@ export declare type CachedClientInfo = {
|
|||
status: ClientStatusInfo;
|
||||
forumAccount: ClientForumInfo | undefined;
|
||||
channelGroup: number;
|
||||
channelGroupInheritedChannel: number;
|
||||
serverGroups: number[];
|
||||
version: ClientVersionInfo;
|
||||
};
|
||||
|
@ -52,5 +53,6 @@ export declare class SelectedClientInfo {
|
|||
private updateCachedCountry;
|
||||
private updateCachedVolume;
|
||||
private updateForumAccount;
|
||||
private updateChannelGroup;
|
||||
private initializeClientInfo;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { RegistryKey, RegistryValueType, ValuedRegistryKey } from "tc-shared/settings";
|
||||
export declare class ServerSettings {
|
||||
private cacheServer;
|
||||
private settingsDestroyed;
|
||||
private serverUniqueId;
|
||||
private serverSaveWorker;
|
||||
private serverSettingsUpdated;
|
||||
constructor();
|
||||
destroy(): void;
|
||||
getValue<V extends RegistryValueType, DV>(key: RegistryKey<V>, defaultValue: DV): V | DV;
|
||||
getValue<V extends RegistryValueType>(key: ValuedRegistryKey<V>, defaultValue?: V): V;
|
||||
setValue<T extends RegistryValueType>(key: RegistryKey<T>, value?: T): void;
|
||||
setServerUniqueId(serverUniqueId: string): void;
|
||||
save(): void;
|
||||
}
|
||||
export interface ServerSettingsStorage {
|
||||
get(serverUniqueId: string): string;
|
||||
set(serverUniqueId: string, value: string): any;
|
||||
}
|
||||
export declare function setServerSettingsStorage(storage: ServerSettingsStorage): void;
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Application storage meant for small and medium large internal app states.
|
||||
* Possible data would be non user editable cached values like auth tokens.
|
||||
* Note:
|
||||
* 1. Please consider using a Settings key first before using the storage adapter!
|
||||
* 2. The values may not be synced across multiple window instances.
|
||||
* Don't use this for IPC.
|
||||
*/
|
||||
export interface StorageAdapter {
|
||||
has(key: string): Promise<boolean>;
|
||||
get(key: string): Promise<string | null>;
|
||||
set(key: string, value: string): Promise<void>;
|
||||
delete(key: string): Promise<void>;
|
||||
}
|
||||
export declare function getStorageAdapter(): StorageAdapter;
|
||||
export declare function setStorageAdapter(adapter: StorageAdapter): void;
|
|
@ -3,6 +3,10 @@ export interface OutputDevice {
|
|||
driver: string;
|
||||
name: string;
|
||||
}
|
||||
export declare namespace OutputDevice {
|
||||
const NoDeviceId = "none";
|
||||
const DefaultDeviceId = "default";
|
||||
}
|
||||
export interface AudioBackendEvents {
|
||||
notify_initialized: {};
|
||||
notify_volume_changed: {
|
||||
|
|
|
@ -4,8 +4,6 @@ export interface AudioRecorderBacked {
|
|||
createInput(): AbstractInput;
|
||||
createLevelMeter(device: InputDevice): Promise<LevelMeter>;
|
||||
getDeviceList(): DeviceList;
|
||||
isRnNoiseSupported(): boolean;
|
||||
toggleRnNoise(target: boolean): any;
|
||||
}
|
||||
export interface DeviceListEvents {
|
||||
notify_list_updated: {
|
||||
|
|
|
@ -59,13 +59,13 @@ export interface SoundFile {
|
|||
export declare function get_sound_volume(sound: Sound, default_volume?: number): number;
|
||||
export declare function set_sound_volume(sound: Sound, volume: number): void;
|
||||
export declare function get_master_volume(): number;
|
||||
export declare function set_master_volume(volume: number): void;
|
||||
export declare function setSoundMasterVolume(volume: number): void;
|
||||
export declare function overlap_activated(): boolean;
|
||||
export declare function set_overlap_activated(flag: boolean): void;
|
||||
export declare function ignore_output_muted(): boolean;
|
||||
export declare function set_ignore_output_muted(flag: boolean): void;
|
||||
export declare function save(): void;
|
||||
export declare function initialize(): Promise<void>;
|
||||
export declare function initializeSounds(): Promise<void>;
|
||||
export interface PlaybackOptions {
|
||||
ignore_muted?: boolean;
|
||||
ignore_overlap?: boolean;
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import { ConnectionHandler } from "tc-shared/ConnectionHandler";
|
||||
export declare type ClientInfoResult = {
|
||||
status: "success";
|
||||
clientName: string;
|
||||
clientUniqueId: string;
|
||||
clientDatabaseId: number;
|
||||
} | {
|
||||
status: "not-found";
|
||||
} | {
|
||||
status: "error";
|
||||
error: string;
|
||||
};
|
||||
export declare class ClientInfoResolver {
|
||||
private readonly handler;
|
||||
private readonly requestDatabaseIds;
|
||||
private readonly requestUniqueIds;
|
||||
private executed;
|
||||
constructor(handler: ConnectionHandler);
|
||||
private registerRequest;
|
||||
private fullFullAllRequests;
|
||||
private static parseClientInfo;
|
||||
getInfoByDatabaseId(databaseId: number): Promise<ClientInfoResult>;
|
||||
getInfoByUniqueId(uniqueId: string): Promise<ClientInfoResult>;
|
||||
executeQueries(): Promise<void>;
|
||||
}
|
|
@ -6,6 +6,7 @@ import { WhisperSession, WhisperTarget } from "../voice/VoiceWhisper";
|
|||
export declare class DummyVoiceConnection extends AbstractVoiceConnection {
|
||||
private recorder;
|
||||
private voiceClients;
|
||||
private triggerUnmountEvent;
|
||||
constructor(connection: AbstractServerConnection);
|
||||
acquireVoiceRecorder(recorder: RecorderProfile | undefined): Promise<void>;
|
||||
availableVoiceClients(): VoiceClient[];
|
||||
|
|
|
@ -14,7 +14,7 @@ export declare abstract class PluginCmdHandler {
|
|||
handleHandlerRegistered(): void;
|
||||
getChannel(): string;
|
||||
abstract handlePluginCommand(data: string, invoker: PluginCommandInvoker): any;
|
||||
protected sendPluginCommand(data: string, mode: "server" | "view" | "channel" | "private", clientId?: number): Promise<CommandResult>;
|
||||
protected sendPluginCommand(data: string, mode: "server" | "view" | "channel" | "private", clientOrChannelId?: number): Promise<CommandResult>;
|
||||
}
|
||||
export declare class PluginCmdRegistry {
|
||||
readonly connection: ConnectionHandler;
|
||||
|
|
|
@ -97,6 +97,7 @@ export declare class RTCConnection {
|
|||
restartConnection(): void;
|
||||
reset(updateConnectionState: boolean): void;
|
||||
setTrackSource(type: RTCSourceTrackType, source: MediaStreamTrack | null): Promise<MediaStreamTrack>;
|
||||
clearTrackSources(types: RTCSourceTrackType[]): Promise<MediaStreamTrack[]>;
|
||||
startVideoBroadcast(type: VideoBroadcastType, config: VideoBroadcastConfig): Promise<void>;
|
||||
changeVideoBroadcastConfig(type: VideoBroadcastType, config: VideoBroadcastConfig): Promise<void>;
|
||||
startAudioBroadcast(): Promise<void>;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { ConnectionHandler } from "../ConnectionHandler";
|
||||
import { Registry } from "../events";
|
||||
import { VideoBroadcastType } from "tc-shared/connection/VideoConnection";
|
||||
export declare type PermissionEditorTab = "groups-server" | "groups-channel" | "channel" | "client" | "client-channel";
|
||||
import { PermissionEditorTab } from "tc-shared/ui/modal/permission/ModalDefinitions";
|
||||
export interface ClientGlobalControlEvents {
|
||||
action_open_window: {
|
||||
window: "settings" | /* use action_open_window_settings! */ "about" | "settings-registry" | "css-variable-editor" | "bookmark-manage" | "query-manage" | "query-create" | "ban-list" | "permissions" | "token-list" | "token-use" | "server-echo-test";
|
||||
|
|
|
@ -38,7 +38,8 @@ export declare abstract class ClientAvatar {
|
|||
setLoading(): void;
|
||||
setLoaded(data: AvatarStateData["loaded"]): void;
|
||||
setErrored(data: AvatarStateData["errored"]): void;
|
||||
awaitLoaded(): Promise<void>;
|
||||
awaitLoaded(): Promise<true>;
|
||||
awaitLoaded(timeout: number): Promise<boolean>;
|
||||
getState(): AvatarState;
|
||||
getStateData(): AvatarStateData[AvatarState];
|
||||
getAvatarHash(): string | "unknown";
|
||||
|
|
|
@ -6,7 +6,7 @@ export declare enum ImageType {
|
|||
SVG = 4,
|
||||
JPEG = 5
|
||||
}
|
||||
export declare function imageType2MediaType(type: ImageType, file?: boolean): "svg" | "jpeg" | "png" | "bmp" | "gif" | "svg+xml";
|
||||
export declare function imageType2MediaType(type: ImageType, file?: boolean): "svg" | "bmp" | "gif" | "svg+xml" | "jpeg" | "png";
|
||||
export declare function responseImageType(encoded_data: string | ArrayBuffer, base64_encoded?: boolean): ImageType;
|
||||
export declare type ImageCacheState = {
|
||||
state: "loaded";
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
export declare type LocalAvatarInfo = {
|
||||
fileName: string;
|
||||
fileSize: number;
|
||||
fileHashMD5: string;
|
||||
fileUploaded: number;
|
||||
fileModified: number;
|
||||
contentType: string;
|
||||
resourceUrl: string | undefined;
|
||||
};
|
||||
export declare type LocalAvatarUpdateResult = {
|
||||
status: "success";
|
||||
} | {
|
||||
status: "error";
|
||||
reason: string;
|
||||
} | {
|
||||
status: "cache-unavailable";
|
||||
};
|
||||
export declare type LocalAvatarLoadResult<T> = {
|
||||
status: "success";
|
||||
result: T;
|
||||
} | {
|
||||
status: "error";
|
||||
reason: string;
|
||||
} | {
|
||||
status: "cache-unavailable" | "empty-result";
|
||||
};
|
||||
export declare type OwnAvatarMode = "uploading" | "server";
|
||||
export declare class OwnAvatarStorage {
|
||||
private openedCache;
|
||||
private static generateRequestUrl;
|
||||
initialize(): Promise<void>;
|
||||
private loadAvatarRequest;
|
||||
loadAvatarImage(serverUniqueId: string, mode: OwnAvatarMode): Promise<LocalAvatarLoadResult<ArrayBuffer>>;
|
||||
loadAvatar(serverUniqueId: string, mode: OwnAvatarMode, createResourceUrl: boolean): Promise<LocalAvatarLoadResult<LocalAvatarInfo>>;
|
||||
updateAvatar(serverUniqueId: string, mode: OwnAvatarMode, target: File): Promise<LocalAvatarUpdateResult>;
|
||||
removeAvatar(serverUniqueId: string, mode: OwnAvatarMode): Promise<void>;
|
||||
/**
|
||||
* Move the avatar file which is currently in "uploading" state to server
|
||||
* @param serverUniqueId
|
||||
*/
|
||||
avatarUploadSucceeded(serverUniqueId: string): Promise<void>;
|
||||
}
|
||||
export declare function getOwnAvatarStorage(): OwnAvatarStorage;
|
|
@ -1,2 +1,6 @@
|
|||
export declare const downloadTextAsFile: (text: string, name: string) => void;
|
||||
export declare const requestFile: (options: {
|
||||
accept?: string;
|
||||
multiple?: boolean;
|
||||
}) => Promise<File[]>;
|
||||
export declare const requestFileAsText: () => Promise<string>;
|
||||
|
|
|
@ -1 +1,14 @@
|
|||
export declare function country_name(alpha_code: string, fallback?: string): string;
|
||||
import "svg-sprites/country-flags";
|
||||
import { CountryFlag } from "svg-sprites/country-flags";
|
||||
interface CountryInfo {
|
||||
name: string;
|
||||
alpha_2: string;
|
||||
alpha_3: string;
|
||||
un_code: number;
|
||||
icon: string;
|
||||
flagMissingWarned?: boolean;
|
||||
}
|
||||
export declare function getKnownCountries(): CountryInfo[];
|
||||
export declare function getCountryName(alphaCode: string, fallback?: string): string;
|
||||
export declare function getCountryFlag(alphaCode: string): CountryFlag;
|
||||
export {};
|
||||
|
|
|
@ -4,7 +4,6 @@ import { ConnectRequestData } from "tc-shared/ipc/ConnectHandler";
|
|||
import "svg-sprites/client-icons";
|
||||
import "../css/load-css";
|
||||
import "./proto";
|
||||
import "./video-viewer/Controller";
|
||||
import "./profiles/ConnectionProfile";
|
||||
import "./update/UpdaterWeb";
|
||||
import "./file/LocalIcons";
|
||||
|
@ -17,6 +16,8 @@ import "./media/Video";
|
|||
import "./ui/AppController";
|
||||
import "./ui/frames/menu-bar/MainMenu";
|
||||
import "./ui/modal/connect/Controller";
|
||||
import "./ui/modal/video-viewer/Controller";
|
||||
import "./ui/modal/avatar-upload/Controller";
|
||||
import "./ui/elements/ContextDivider";
|
||||
import "./ui/elements/Tab";
|
||||
import "./clientservice";
|
||||
|
|
|
@ -72,3 +72,12 @@ export declare type ReadonlyKeys<T> = {
|
|||
-readonly [Q in P]: T[P];
|
||||
}, never, P>;
|
||||
}[keyof T];
|
||||
export declare function crashOnThrow<T>(promise: Promise<T> | (() => Promise<T>)): Promise<T>;
|
||||
export declare function ignorePromise<T>(_promise: Promise<T>): void;
|
||||
export declare function NoThrow(target: any, methodName: string, descriptor: PropertyDescriptor): void;
|
||||
export declare function CallOnce(target: any, methodName: string, descriptor: PropertyDescriptor): void;
|
||||
export declare function NonNull(target: any, methodName: string, parameterIndex: number): void;
|
||||
/**
|
||||
* The class or method has been constrained
|
||||
*/
|
||||
export declare function ParameterConstrained(target: any, methodName: string, descriptor: PropertyDescriptor): void;
|
||||
|
|
|
@ -15,6 +15,8 @@ export interface RegistryKey<ValueType extends RegistryValueType> {
|
|||
export interface ValuedRegistryKey<ValueType extends RegistryValueType> extends RegistryKey<ValueType> {
|
||||
defaultValue: ValueType;
|
||||
}
|
||||
export declare function encodeSettingValueToString<T extends RegistryValueType>(input: T): string;
|
||||
export declare function resolveSettingKey<ValueType extends RegistryValueType, DefaultType>(key: RegistryKey<ValueType>, resolver: (key: string) => string | undefined | null, defaultValue: DefaultType): ValueType | DefaultType;
|
||||
export declare class UrlParameterParser {
|
||||
private readonly url;
|
||||
constructor(url: URL);
|
||||
|
@ -53,14 +55,6 @@ export declare namespace AppParameters {
|
|||
const KEY_MODAL_IPC_CHANNEL: RegistryKey<string>;
|
||||
const KEY_LOAD_DUMMY_ERROR: ValuedRegistryKey<boolean>;
|
||||
}
|
||||
export declare class StaticSettings {
|
||||
private static _instance;
|
||||
static get instance(): StaticSettings;
|
||||
protected staticValues: {};
|
||||
protected constructor(_reserved?: any);
|
||||
static<V extends RegistryValueType, DV>(key: RegistryKey<V>, defaultValue: DV): V | DV;
|
||||
static<V extends RegistryValueType>(key: ValuedRegistryKey<V>, defaultValue?: V): V;
|
||||
}
|
||||
export interface SettingsEvents {
|
||||
notify_setting_changed: {
|
||||
setting: string;
|
||||
|
@ -145,6 +139,9 @@ export declare class Settings {
|
|||
static readonly KEY_MICROPHONE_THRESHOLD_ATTACK_SMOOTH: ValuedRegistryKey<number>;
|
||||
static readonly KEY_MICROPHONE_THRESHOLD_RELEASE_SMOOTH: ValuedRegistryKey<number>;
|
||||
static readonly KEY_MICROPHONE_THRESHOLD_RELEASE_DELAY: ValuedRegistryKey<number>;
|
||||
static readonly KEY_SPEAKER_DEVICE_ID: RegistryKey<string>;
|
||||
static readonly KEY_UPDATER_LAST_USED_UI: RegistryKey<string>;
|
||||
static readonly KEY_UPDATER_LAST_USED_CLIENT: RegistryKey<string>;
|
||||
static readonly FN_LOG_ENABLED: (category: string) => RegistryKey<boolean>;
|
||||
static readonly FN_SEPARATOR_STATE: (separator: string) => RegistryKey<string>;
|
||||
static readonly FN_LOG_LEVEL_ENABLED: (category: string) => RegistryKey<boolean>;
|
||||
|
@ -159,30 +156,18 @@ export declare class Settings {
|
|||
static readonly FN_EVENTS_LOG_ENABLED: (event: string) => RegistryKey<boolean>;
|
||||
static readonly FN_EVENTS_FOCUS_ENABLED: (event: string) => RegistryKey<boolean>;
|
||||
static readonly KEYS: any[];
|
||||
static initialize(): void;
|
||||
readonly events: Registry<SettingsEvents>;
|
||||
private readonly cacheGlobal;
|
||||
private settingsCache;
|
||||
private saveWorker;
|
||||
private updated;
|
||||
private saveState;
|
||||
constructor();
|
||||
initialize(): Promise<void>;
|
||||
getValue<V extends RegistryValueType, DV>(key: RegistryKey<V>, defaultValue: DV): V | DV;
|
||||
getValue<V extends RegistryValueType>(key: ValuedRegistryKey<V>, defaultValue?: V): V;
|
||||
setValue<T extends RegistryValueType>(key: RegistryKey<T>, value?: T): void;
|
||||
globalChangeListener<T extends RegistryValueType>(key: RegistryKey<T>, listener: (newValue: T) => void): () => void;
|
||||
save(): void;
|
||||
}
|
||||
export declare class ServerSettings {
|
||||
private cacheServer;
|
||||
private serverUniqueId;
|
||||
private serverSaveWorker;
|
||||
private serverSettingsUpdated;
|
||||
private _destroyed;
|
||||
constructor();
|
||||
destroy(): void;
|
||||
getValue<V extends RegistryValueType, DV>(key: RegistryKey<V>, defaultValue: DV): V | DV;
|
||||
getValue<V extends RegistryValueType>(key: ValuedRegistryKey<V>, defaultValue?: V): V;
|
||||
setValue<T extends RegistryValueType>(key: RegistryKey<T>, value?: T): void;
|
||||
setServer(server_unique_id: string): void;
|
||||
private doSave;
|
||||
save(): void;
|
||||
}
|
||||
export declare let settings: Settings;
|
||||
|
|
|
@ -4,7 +4,7 @@ import ReactRenderer from "vendor/xbbcode/renderer/react";
|
|||
import HTMLRenderer from "vendor/xbbcode/renderer/html";
|
||||
import "./emoji";
|
||||
import "./highlight";
|
||||
import "./YoutubeRenderer";
|
||||
import "./YoutubeController";
|
||||
import "./url";
|
||||
import "./image";
|
||||
export declare let BBCodeHandlerContext: Context<string>;
|
||||
|
|
|
@ -100,9 +100,7 @@ export declare class ClientConnectionInfo {
|
|||
}
|
||||
export interface ClientEvents extends ChannelTreeEntryEvents {
|
||||
notify_properties_updated: {
|
||||
updated_properties: {
|
||||
[Key in keyof ClientProperties]: ClientProperties[Key];
|
||||
};
|
||||
updated_properties: Partial<ClientProperties>;
|
||||
client_properties: ClientProperties;
|
||||
};
|
||||
notify_mute_state_change: {
|
||||
|
@ -166,8 +164,10 @@ export declare class ClientEntry<Events extends ClientEvents = ClientEvents> ext
|
|||
static chatTag(id: number, name: string, uid: string, braces?: boolean): JQuery;
|
||||
create_bbcode(): string;
|
||||
createChatTag(braces?: boolean): JQuery;
|
||||
set speaking(flag: any);
|
||||
/** @deprecated Don't use this any more! */
|
||||
set speaking(flag: boolean);
|
||||
isSpeaking(): boolean;
|
||||
protected setSpeaking(flag: boolean): void;
|
||||
updateVariables(...variables: {
|
||||
key: string;
|
||||
value: string;
|
||||
|
@ -188,6 +188,7 @@ export declare class ClientEntry<Events extends ClientEvents = ClientEvents> ext
|
|||
export declare class LocalClientEntry extends ClientEntry {
|
||||
handle: ConnectionHandler;
|
||||
constructor(handle: ConnectionHandler);
|
||||
setSpeaking(flag: boolean): void;
|
||||
showContextMenu(x: number, y: number, on_close?: () => void): void;
|
||||
renameSelf(new_name: string): Promise<boolean>;
|
||||
openRenameModal(): void;
|
||||
|
|
|
@ -28,6 +28,7 @@ declare class InfoController {
|
|||
sendMicrophoneState(): void;
|
||||
sendMicrophoneList(): void;
|
||||
sendSpeakerState(): void;
|
||||
sendSpeakerList(): Promise<void>;
|
||||
sendSubscribeState(): void;
|
||||
sendQueryState(): void;
|
||||
sendHostButton(): void;
|
||||
|
|
|
@ -27,7 +27,7 @@ export declare type VideoDeviceInfo = {
|
|||
name: string;
|
||||
id: string;
|
||||
};
|
||||
export declare type MicrophoneDeviceInfo = {
|
||||
export declare type AudioDeviceInfo = {
|
||||
name: string;
|
||||
id: string;
|
||||
driver: string;
|
||||
|
@ -57,6 +57,7 @@ export interface ControlBarEvents {
|
|||
};
|
||||
action_toggle_speaker: {
|
||||
enabled: boolean;
|
||||
targetDeviceId?: string;
|
||||
};
|
||||
action_toggle_subscribe: {
|
||||
subscribe: boolean;
|
||||
|
@ -75,6 +76,7 @@ export interface ControlBarEvents {
|
|||
broadcastType: VideoBroadcastType;
|
||||
};
|
||||
action_open_microphone_settings: {};
|
||||
action_open_speaker_settings: {};
|
||||
query_mode: {};
|
||||
query_connection_state: {};
|
||||
query_bookmarks: {};
|
||||
|
@ -82,6 +84,7 @@ export interface ControlBarEvents {
|
|||
query_microphone_state: {};
|
||||
query_microphone_list: {};
|
||||
query_speaker_state: {};
|
||||
query_speaker_list: {};
|
||||
query_subscribe_state: {};
|
||||
query_query_state: {};
|
||||
query_host_button: {};
|
||||
|
@ -105,11 +108,17 @@ export interface ControlBarEvents {
|
|||
state: MicrophoneState;
|
||||
};
|
||||
notify_microphone_list: {
|
||||
devices: MicrophoneDeviceInfo[];
|
||||
devices: AudioDeviceInfo[];
|
||||
};
|
||||
notify_speaker_state: {
|
||||
enabled: boolean;
|
||||
};
|
||||
notify_speaker_list: {
|
||||
state: "initialized";
|
||||
devices: AudioDeviceInfo[];
|
||||
} | {
|
||||
state: "uninitialized";
|
||||
};
|
||||
notify_subscribe_state: {
|
||||
subscribe: boolean;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import * as React from "react";
|
||||
import { ReactComponentBase } from "tc-shared/ui/react-elements/ReactComponentBase";
|
||||
import { RemoteIconInfo } from "tc-shared/file/Icons";
|
||||
export interface DropdownEntryProperties {
|
||||
icon?: string | RemoteIconInfo;
|
||||
|
@ -7,10 +6,14 @@ export interface DropdownEntryProperties {
|
|||
onClick?: (event: React.MouseEvent) => void;
|
||||
onAuxClick?: (event: React.MouseEvent) => void;
|
||||
onContextMenu?: (event: React.MouseEvent) => void;
|
||||
children?: React.ReactElement<DropdownEntry>[];
|
||||
children?: React.ReactElement<DropdownEntry | DropdownTitleEntry>[];
|
||||
}
|
||||
export declare class DropdownEntry extends ReactComponentBase<DropdownEntryProperties, {}> {
|
||||
protected defaultState(): {};
|
||||
export declare class DropdownEntry extends React.PureComponent<DropdownEntryProperties> {
|
||||
render(): JSX.Element;
|
||||
}
|
||||
export declare class DropdownTitleEntry extends React.PureComponent<{
|
||||
children: any;
|
||||
}> {
|
||||
render(): JSX.Element;
|
||||
}
|
||||
export declare const DropdownContainer: (props: {
|
||||
|
|
|
@ -15,7 +15,6 @@ export declare abstract class AbstractConversationController<Events extends Abst
|
|||
protected registerConversationManagerEvents(manager: Manager): void;
|
||||
protected registerConversationEvents(conversation: ConversationType): void;
|
||||
protected setCurrentlySelected(conversation: ConversationType | undefined): void;
|
||||
handlePanelShow(): void;
|
||||
protected reportStateToUI(conversation: AbstractChat<any>): void;
|
||||
uiQueryHistory(conversation: AbstractChat<any>, timestamp: number, enforce?: boolean): void;
|
||||
protected getCurrentConversation(): ConversationType | undefined;
|
||||
|
|
|
@ -121,7 +121,6 @@ export interface AbstractConversationUiEvents {
|
|||
notify_selected_chat: {
|
||||
chatId: "unselected" | string;
|
||||
};
|
||||
notify_panel_show: {};
|
||||
notify_chat_event: {
|
||||
chatId: string;
|
||||
triggerUnread: boolean;
|
||||
|
|
|
@ -8,7 +8,6 @@ export declare class ChannelConversationController extends AbstractConversationC
|
|||
constructor();
|
||||
destroy(): void;
|
||||
setConnectionHandler(connection: ConnectionHandler): void;
|
||||
private initializeConnectionListener;
|
||||
private handleMessageDelete;
|
||||
protected registerConversationEvents(conversation: ChannelConversation): void;
|
||||
}
|
||||
|
|
|
@ -3,10 +3,13 @@ export declare class ClientInfoController {
|
|||
private readonly uiEvents;
|
||||
private connection;
|
||||
private listenerConnection;
|
||||
private listenerInheritedChannel;
|
||||
private inheritedChannelInfo;
|
||||
constructor();
|
||||
destroy(): void;
|
||||
setConnectionHandler(connection: ConnectionHandler): void;
|
||||
private initializeConnection;
|
||||
private updateInheritedInfo;
|
||||
private generateGroupInfo;
|
||||
private sendClient;
|
||||
private sendChannelGroup;
|
||||
|
|
|
@ -46,6 +46,10 @@ export declare type ClientVersionInfo = {
|
|||
platform: string;
|
||||
version: string;
|
||||
};
|
||||
export declare type InheritedChannelInfo = {
|
||||
channelId: number;
|
||||
channelName: string;
|
||||
};
|
||||
export interface ClientInfoEvents {
|
||||
action_show_full_info: {};
|
||||
action_edit_avatar: {};
|
||||
|
@ -68,6 +72,7 @@ export interface ClientInfoEvents {
|
|||
};
|
||||
notify_channel_group: {
|
||||
group: ClientGroupInfo | undefined;
|
||||
inheritedChannel: InheritedChannelInfo | undefined;
|
||||
};
|
||||
notify_server_groups: {
|
||||
groups: ClientGroupInfo[];
|
||||
|
|
|
@ -15,7 +15,6 @@ export declare class PrivateConversationController extends AbstractConversationC
|
|||
constructor();
|
||||
destroy(): void;
|
||||
setConnectionHandler(connection: ConnectionHandler): void;
|
||||
private initializeConnectionListener;
|
||||
protected registerConversationManagerEvents(manager: PrivateConversationManager): void;
|
||||
focusInput(): void;
|
||||
private reportConversationList;
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export declare function spawnAvatarUpload(callback_data: (data: ArrayBuffer | undefined | null) => any): void;
|
|
@ -1,6 +0,0 @@
|
|||
export declare const Regex: {
|
||||
DOMAIN: RegExp;
|
||||
IP_V4: RegExp;
|
||||
IP_V6: RegExp;
|
||||
IP: RegExp;
|
||||
};
|
|
@ -1,2 +0,0 @@
|
|||
import { ClientEntry } from "../../tree/Client";
|
||||
export declare function createServerGroupAssignmentModal(client: ClientEntry, callback: (groups: number[], flag: boolean) => Promise<boolean>): void;
|
|
@ -0,0 +1 @@
|
|||
export declare function spawnAboutModal(): void;
|
|
@ -0,0 +1,15 @@
|
|||
export interface ModalAboutVariables {
|
||||
readonly uiVersion: string;
|
||||
readonly uiVersionTimestamp: number;
|
||||
readonly nativeVersion: string;
|
||||
eggShown: boolean;
|
||||
}
|
||||
export interface ModalAboutEvents {
|
||||
action_update_high_score: {
|
||||
score: number;
|
||||
};
|
||||
query_high_score: {};
|
||||
notify_high_score: {
|
||||
score: number;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import { AbstractModal } from "tc-shared/ui/react-elements/modal/Definitions";
|
||||
import React from "react";
|
||||
import { IpcRegistryDescription } from "tc-events";
|
||||
import { ModalAboutEvents, ModalAboutVariables } from "tc-shared/ui/modal/about/Definitions";
|
||||
import { IpcVariableDescriptor } from "tc-shared/ui/utils/IpcVariable";
|
||||
declare class Modal extends AbstractModal {
|
||||
private readonly events;
|
||||
private readonly variables;
|
||||
constructor(events: IpcRegistryDescription<ModalAboutEvents>, variables: IpcVariableDescriptor<ModalAboutVariables>);
|
||||
renderBody(): React.ReactElement;
|
||||
renderTitle(): string | React.ReactElement;
|
||||
}
|
||||
export default Modal;
|
|
@ -0,0 +1,2 @@
|
|||
import { ConnectionHandler } from "tc-shared/ConnectionHandler";
|
||||
export declare function spawnAvatarUpload(connection: ConnectionHandler): void;
|
|
@ -0,0 +1,33 @@
|
|||
export declare type CurrentAvatarState = {
|
||||
status: "unset" | "loading";
|
||||
} | {
|
||||
status: "available" | "exceeds-max-size";
|
||||
fileName: string;
|
||||
fileSize: number;
|
||||
fileHashMD5: string;
|
||||
resourceUrl: string | undefined;
|
||||
serverHasAvatar: boolean;
|
||||
} | {
|
||||
status: "server";
|
||||
resourceUrl: string;
|
||||
};
|
||||
export interface ModalAvatarUploadVariables {
|
||||
readonly maxAvatarSize: number;
|
||||
readonly currentAvatar: CurrentAvatarState;
|
||||
}
|
||||
export interface ModalAvatarUploadEvents {
|
||||
action_open_select: {};
|
||||
action_file_cache_loading: {};
|
||||
action_file_cache_loading_finished: {
|
||||
success: boolean;
|
||||
};
|
||||
action_avatar_upload: {
|
||||
closeWindow: boolean;
|
||||
};
|
||||
action_avatar_delete: {
|
||||
closeWindow: boolean;
|
||||
};
|
||||
notify_avatar_load_error: {
|
||||
error: string;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import { AbstractModal } from "tc-shared/ui/react-elements/modal/Definitions";
|
||||
import React from "react";
|
||||
import { IpcRegistryDescription } from "tc-events";
|
||||
import { ModalAvatarUploadEvents, ModalAvatarUploadVariables } from "tc-shared/ui/modal/avatar-upload/Definitions";
|
||||
import { IpcVariableDescriptor } from "tc-shared/ui/utils/IpcVariable";
|
||||
declare class ModalAvatarUpload extends AbstractModal {
|
||||
private readonly serverUniqueId;
|
||||
private readonly events;
|
||||
private readonly variables;
|
||||
constructor(events: IpcRegistryDescription<ModalAvatarUploadEvents>, variables: IpcVariableDescriptor<ModalAvatarUploadVariables>, serverUniqueId: string);
|
||||
protected onDestroy(): void;
|
||||
renderBody(): React.ReactElement;
|
||||
renderTitle(): string | React.ReactElement;
|
||||
}
|
||||
export default ModalAvatarUpload;
|
|
@ -0,0 +1,2 @@
|
|||
import { ConnectionHandler } from "tc-shared/ConnectionHandler";
|
||||
export declare function spawnServerGroupAssignments(handler: ConnectionHandler, targetClientDatabaseId: number): void;
|
|
@ -0,0 +1,57 @@
|
|||
import { RemoteIconInfo } from "tc-shared/file/Icons";
|
||||
export declare type AvailableGroup = {
|
||||
groupId: number;
|
||||
saveDB: boolean;
|
||||
name: string;
|
||||
icon: RemoteIconInfo | undefined;
|
||||
addAble: boolean;
|
||||
removeAble: boolean;
|
||||
};
|
||||
export declare type ClientInfo = {
|
||||
status: "success";
|
||||
clientDatabaseId: number;
|
||||
clientUniqueId: string;
|
||||
clientName: string;
|
||||
} | {
|
||||
status: "error";
|
||||
message: string;
|
||||
};
|
||||
export interface ModalClientGroupAssignmentVariables {
|
||||
readonly handlerId: string;
|
||||
readonly targetClient: ClientInfo;
|
||||
readonly availableGroups: {
|
||||
groups: AvailableGroup[];
|
||||
defaultGroup: number;
|
||||
};
|
||||
readonly assignedGroupStatus: {
|
||||
status: "loaded";
|
||||
assignedGroups: number;
|
||||
} | {
|
||||
status: "loading";
|
||||
} | {
|
||||
status: "error";
|
||||
message: string;
|
||||
};
|
||||
groupAssigned: boolean;
|
||||
}
|
||||
export interface ModalClientGroupAssignmentEvents {
|
||||
action_close: {};
|
||||
action_remove_all: {};
|
||||
action_refresh: {
|
||||
slowMode: boolean;
|
||||
};
|
||||
notify_toggle_result: {
|
||||
action: "add" | "remove";
|
||||
groupId: number;
|
||||
groupName: string;
|
||||
result: {
|
||||
status: "success";
|
||||
} | {
|
||||
status: "error";
|
||||
reason: string;
|
||||
} | {
|
||||
status: "no-permissions";
|
||||
permission: string;
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import { AbstractModal } from "tc-shared/ui/react-elements/modal/Definitions";
|
||||
import React from "react";
|
||||
import { IpcRegistryDescription } from "tc-events";
|
||||
import { ModalClientGroupAssignmentEvents, ModalClientGroupAssignmentVariables } from "tc-shared/ui/modal/group-assignment/Definitions";
|
||||
import { IpcVariableDescriptor } from "tc-shared/ui/utils/IpcVariable";
|
||||
export default class ModalServerGroups extends AbstractModal {
|
||||
private readonly events;
|
||||
private readonly variables;
|
||||
constructor(events: IpcRegistryDescription<ModalClientGroupAssignmentEvents>, variables: IpcVariableDescriptor<ModalClientGroupAssignmentVariables>);
|
||||
protected onDestroy(): void;
|
||||
renderBody(): React.ReactElement;
|
||||
renderTitle(): string | React.ReactElement;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
import { InputProcessor } from "tc-shared/voice/RecorderBase";
|
||||
export declare function spawnInputProcessorModal(processor: InputProcessor): void;
|
|
@ -0,0 +1,13 @@
|
|||
import { InputProcessorConfigRNNoise, InputProcessorConfigWebRTC, InputProcessorStatistics } from "tc-shared/voice/RecorderBase";
|
||||
export declare type ModalInputProcessorVariables = {
|
||||
propertyFilter: string;
|
||||
} & InputProcessorConfigRNNoise & InputProcessorConfigWebRTC;
|
||||
export interface ModalInputProcessorEvents {
|
||||
query_statistics: {};
|
||||
notify_statistics: {
|
||||
statistics: InputProcessorStatistics;
|
||||
};
|
||||
notify_apply_error: {
|
||||
message: string;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import { AbstractModal } from "tc-shared/ui/react-elements/modal/Definitions";
|
||||
import React from "react";
|
||||
import { IpcRegistryDescription } from "tc-events";
|
||||
import { ModalInputProcessorEvents, ModalInputProcessorVariables } from "tc-shared/ui/modal/input-processor/Definitios";
|
||||
import { IpcVariableDescriptor } from "tc-shared/ui/utils/IpcVariable";
|
||||
declare class Modal extends AbstractModal {
|
||||
private readonly events;
|
||||
private readonly variables;
|
||||
constructor(events: IpcRegistryDescription<ModalInputProcessorEvents>, variables: IpcVariableDescriptor<ModalInputProcessorVariables>);
|
||||
protected onDestroy(): void;
|
||||
renderBody(): React.ReactElement;
|
||||
renderTitle(): string | React.ReactElement;
|
||||
}
|
||||
export default Modal;
|
|
@ -1,6 +1,3 @@
|
|||
import * as React from "react";
|
||||
import { Registry } from "tc-shared/events";
|
||||
import { ConnectionHandler } from "tc-shared/ConnectionHandler";
|
||||
export interface EditorGroupedPermissions {
|
||||
groupId: string;
|
||||
groupName: string;
|
||||
|
@ -11,7 +8,7 @@ export interface EditorGroupedPermissions {
|
|||
}[];
|
||||
children: EditorGroupedPermissions[];
|
||||
}
|
||||
declare type PermissionEditorMode = "unset" | "no-permissions" | "normal";
|
||||
export declare type PermissionEditorMode = "unset" | "no-permissions" | "normal";
|
||||
export interface PermissionEditorEvents {
|
||||
action_set_mode: {
|
||||
mode: PermissionEditorMode;
|
||||
|
@ -104,15 +101,3 @@ export interface PermissionEditorEvents {
|
|||
}[];
|
||||
};
|
||||
}
|
||||
interface PermissionEditorProperties {
|
||||
connection: ConnectionHandler;
|
||||
events: Registry<PermissionEditorEvents>;
|
||||
}
|
||||
interface PermissionEditorState {
|
||||
state: "no-permissions" | "unset" | "normal";
|
||||
}
|
||||
export declare class PermissionEditor extends React.Component<PermissionEditorProperties, PermissionEditorState> {
|
||||
render(): JSX.Element[];
|
||||
componentDidMount(): void;
|
||||
}
|
||||
export {};
|
|
@ -0,0 +1,16 @@
|
|||
import * as React from "react";
|
||||
import { Registry } from "tc-shared/events";
|
||||
import { PermissionEditorEvents } from "tc-shared/ui/modal/permission/EditorDefinitions";
|
||||
interface PermissionEditorProperties {
|
||||
handlerId: string;
|
||||
serverUniqueId: string;
|
||||
events: Registry<PermissionEditorEvents>;
|
||||
}
|
||||
interface PermissionEditorState {
|
||||
state: "no-permissions" | "unset" | "normal";
|
||||
}
|
||||
export declare class EditorRenderer extends React.Component<PermissionEditorProperties, PermissionEditorState> {
|
||||
render(): JSX.Element;
|
||||
componentDidMount(): void;
|
||||
}
|
||||
export {};
|
|
@ -0,0 +1,4 @@
|
|||
import { ConnectionHandler } from "tc-shared/ConnectionHandler";
|
||||
import { DefaultTabValues } from "tc-shared/ui/modal/permission/ModalRenderer";
|
||||
import { PermissionEditorTab } from "tc-shared/ui/modal/permission/ModalDefinitions";
|
||||
export declare function spawnPermissionEditorModal(connection: ConnectionHandler, defaultTab?: PermissionEditorTab, defaultTabValues?: DefaultTabValues): void;
|
|
@ -1,14 +1,5 @@
|
|||
import { ConnectionHandler } from "tc-shared/ConnectionHandler";
|
||||
import * as React from "react";
|
||||
import { PermissionEditorTab } from "tc-shared/events/GlobalEvents";
|
||||
export declare type PermissionEditorTab = "groups-server" | "groups-channel" | "channel" | "client" | "client-channel";
|
||||
export declare type PermissionEditorSubject = "groups-server" | "groups-channel" | "channel" | "client" | "client-channel" | "none";
|
||||
export declare const PermissionTabName: {
|
||||
[T in PermissionEditorTab]: {
|
||||
name: string;
|
||||
useTranslate: () => string;
|
||||
renderTranslate: () => React.ReactNode;
|
||||
};
|
||||
};
|
||||
export declare type GroupProperties = {
|
||||
id: number;
|
||||
type: "query" | "template" | "normal";
|
||||
|
@ -182,11 +173,6 @@ export interface PermissionModalEvents {
|
|||
property: "name" | "icon";
|
||||
value: any;
|
||||
};
|
||||
notify_initial_rendered: {};
|
||||
notify_destroy: {};
|
||||
}
|
||||
export declare type DefaultTabValues = {
|
||||
groupId?: number;
|
||||
channelId?: number;
|
||||
clientDatabaseId?: number;
|
||||
};
|
||||
export declare function spawnPermissionEditorModal(connection: ConnectionHandler, defaultTab?: PermissionEditorTab, values?: DefaultTabValues): void;
|
|
@ -0,0 +1,32 @@
|
|||
import * as React from "react";
|
||||
import { IpcRegistryDescription, Registry } from "tc-shared/events";
|
||||
import { Translatable } from "tc-shared/ui/react-elements/i18n";
|
||||
import { PermissionEditorEvents } from "tc-shared/ui/modal/permission/EditorDefinitions";
|
||||
import { PermissionEditorTab, PermissionModalEvents } from "tc-shared/ui/modal/permission/ModalDefinitions";
|
||||
import { AbstractModal } from "tc-shared/ui/react-elements/modal/Definitions";
|
||||
export declare type PermissionEditorServerInfo = {
|
||||
handlerId: string;
|
||||
serverUniqueId: string;
|
||||
};
|
||||
export declare const PermissionTabName: {
|
||||
[T in PermissionEditorTab]: {
|
||||
name: string;
|
||||
useTranslate: () => string;
|
||||
renderTranslate: () => React.ReactNode;
|
||||
};
|
||||
};
|
||||
export declare type DefaultTabValues = {
|
||||
groupId?: number;
|
||||
channelId?: number;
|
||||
clientDatabaseId?: number;
|
||||
};
|
||||
export declare class PermissionEditorModal extends AbstractModal {
|
||||
readonly serverInfo: PermissionEditorServerInfo;
|
||||
readonly modalEvents: Registry<PermissionModalEvents>;
|
||||
readonly editorEvents: Registry<PermissionEditorEvents>;
|
||||
constructor(serverInfo: PermissionEditorServerInfo, modalEvents: IpcRegistryDescription<PermissionModalEvents>, editorEvents: IpcRegistryDescription<PermissionEditorEvents>);
|
||||
protected onDestroy(): void;
|
||||
renderBody(): JSX.Element;
|
||||
renderTitle(): React.ReactElement<Translatable>;
|
||||
}
|
||||
export default PermissionEditorModal;
|
|
@ -1,12 +0,0 @@
|
|||
import * as React from "react";
|
||||
import { Registry } from "tc-shared/events";
|
||||
import { PermissionModalEvents } from "tc-shared/ui/modal/permission/ModalPermissionEditor";
|
||||
import { PermissionEditorEvents } from "tc-shared/ui/modal/permission/PermissionEditor";
|
||||
import { ConnectionHandler } from "tc-shared/ConnectionHandler";
|
||||
export declare class SideBar extends React.Component<{
|
||||
connection: ConnectionHandler;
|
||||
modalEvents: Registry<PermissionModalEvents>;
|
||||
editorEvents: Registry<PermissionEditorEvents>;
|
||||
}, {}> {
|
||||
render(): JSX.Element[];
|
||||
}
|
|
@ -1,76 +1,3 @@
|
|||
import { Registry } from "tc-shared/events";
|
||||
import { DeviceListState } from "tc-shared/audio/Recorder";
|
||||
export declare type MicrophoneSetting = "volume" | "vad-type" | "ppt-key" | "ppt-release-delay" | "ppt-release-delay-active" | "threshold-threshold" | "rnnoise";
|
||||
export declare type MicrophoneDevice = {
|
||||
id: string;
|
||||
name: string;
|
||||
driver: string;
|
||||
default: boolean;
|
||||
};
|
||||
export declare type SelectedMicrophone = {
|
||||
type: "default";
|
||||
} | {
|
||||
type: "none";
|
||||
} | {
|
||||
type: "device";
|
||||
deviceId: string;
|
||||
};
|
||||
export declare type MicrophoneDevices = {
|
||||
status: "error";
|
||||
error: string;
|
||||
} | {
|
||||
status: "audio-not-initialized";
|
||||
} | {
|
||||
status: "no-permissions";
|
||||
shouldAsk: boolean;
|
||||
} | {
|
||||
status: "success";
|
||||
devices: MicrophoneDevice[];
|
||||
selectedDevice: SelectedMicrophone;
|
||||
};
|
||||
export interface MicrophoneSettingsEvents {
|
||||
"query_devices": {
|
||||
refresh_list: boolean;
|
||||
};
|
||||
"query_help": {};
|
||||
"query_setting": {
|
||||
setting: MicrophoneSetting;
|
||||
};
|
||||
"action_help_click": {};
|
||||
"action_request_permissions": {};
|
||||
"action_set_selected_device": {
|
||||
target: SelectedMicrophone;
|
||||
};
|
||||
"action_set_selected_device_result": {
|
||||
status: "error";
|
||||
reason: string;
|
||||
};
|
||||
"action_set_setting": {
|
||||
setting: MicrophoneSetting;
|
||||
value: any;
|
||||
};
|
||||
notify_setting: {
|
||||
setting: MicrophoneSetting;
|
||||
value: any;
|
||||
};
|
||||
notify_devices: MicrophoneDevices;
|
||||
notify_device_selected: {
|
||||
device: SelectedMicrophone;
|
||||
};
|
||||
notify_device_level: {
|
||||
level: {
|
||||
[key: string]: {
|
||||
deviceId: string;
|
||||
status: "success" | "error";
|
||||
level?: number;
|
||||
error?: string;
|
||||
};
|
||||
};
|
||||
status: Exclude<DeviceListState, "error">;
|
||||
};
|
||||
notify_highlight: {
|
||||
field: "hs-0" | "hs-1" | "hs-2" | undefined;
|
||||
};
|
||||
notify_destroy: {};
|
||||
}
|
||||
import { MicrophoneSettingsEvents } from "tc-shared/ui/modal/settings/MicrophoneDefinitions";
|
||||
export declare function initialize_audio_microphone_controller(events: Registry<MicrophoneSettingsEvents>): void;
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
import { DeviceListState } from "tc-shared/audio/Recorder";
|
||||
export declare type MicrophoneSetting = "volume" | "vad-type" | "ppt-key" | "ppt-release-delay" | "ppt-release-delay-active" | "threshold-threshold" | "rnnoise";
|
||||
export declare type MicrophoneDevice = {
|
||||
id: string;
|
||||
name: string;
|
||||
driver: string;
|
||||
default: boolean;
|
||||
};
|
||||
export declare type SelectedMicrophone = {
|
||||
type: "default";
|
||||
} | {
|
||||
type: "none";
|
||||
} | {
|
||||
type: "device";
|
||||
deviceId: string;
|
||||
};
|
||||
export declare type MicrophoneDevices = {
|
||||
status: "error";
|
||||
error: string;
|
||||
} | {
|
||||
status: "audio-not-initialized";
|
||||
} | {
|
||||
status: "no-permissions";
|
||||
shouldAsk: boolean;
|
||||
} | {
|
||||
status: "success";
|
||||
devices: MicrophoneDevice[];
|
||||
selectedDevice: SelectedMicrophone;
|
||||
};
|
||||
export declare type InputDeviceLevel = {
|
||||
status: "success";
|
||||
level: number;
|
||||
} | {
|
||||
status: "uninitialized";
|
||||
} | {
|
||||
status: "error";
|
||||
message: string;
|
||||
};
|
||||
export interface MicrophoneSettingsEvents {
|
||||
"query_devices": {
|
||||
refresh_list: boolean;
|
||||
};
|
||||
"query_help": {};
|
||||
"query_setting": {
|
||||
setting: MicrophoneSetting;
|
||||
};
|
||||
"query_input_level": {};
|
||||
"action_help_click": {};
|
||||
"action_request_permissions": {};
|
||||
"action_set_selected_device": {
|
||||
target: SelectedMicrophone;
|
||||
};
|
||||
"action_set_selected_device_result": {
|
||||
status: "error";
|
||||
reason: string;
|
||||
};
|
||||
"action_open_processor_properties": {};
|
||||
"action_set_setting": {
|
||||
setting: MicrophoneSetting;
|
||||
value: any;
|
||||
};
|
||||
notify_setting: {
|
||||
setting: MicrophoneSetting;
|
||||
value: any;
|
||||
};
|
||||
notify_devices: MicrophoneDevices;
|
||||
notify_device_selected: {
|
||||
device: SelectedMicrophone;
|
||||
};
|
||||
notify_device_level: {
|
||||
level: {
|
||||
[key: string]: {
|
||||
deviceId: string;
|
||||
status: "success" | "error";
|
||||
level?: number;
|
||||
error?: string;
|
||||
};
|
||||
};
|
||||
status: Exclude<DeviceListState, "error">;
|
||||
};
|
||||
notify_input_level: {
|
||||
level: InputDeviceLevel;
|
||||
};
|
||||
notify_highlight: {
|
||||
field: "hs-0" | "hs-1" | "hs-2" | undefined;
|
||||
};
|
||||
notify_destroy: {};
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/// <reference types="react" />
|
||||
import { Registry } from "tc-shared/events";
|
||||
import { MicrophoneSettingsEvents } from "tc-shared/ui/modal/settings/Microphone";
|
||||
import { MicrophoneSettingsEvents } from "tc-shared/ui/modal/settings/MicrophoneDefinitions";
|
||||
export declare const MicrophoneSettings: (props: {
|
||||
events: Registry<MicrophoneSettingsEvents>;
|
||||
}) => JSX.Element;
|
||||
|
|
|
@ -32,6 +32,7 @@ export declare class NavigationBar extends ReactComponentBase<NavigationBarPrope
|
|||
private ignoreBlur;
|
||||
private lastSucceededPath;
|
||||
protected defaultState(): NavigationBarState;
|
||||
componentDidMount(): void;
|
||||
render(): JSX.Element;
|
||||
componentDidUpdate(prevProps: Readonly<NavigationBarProperties>, prevState: Readonly<NavigationBarState>, snapshot?: any): void;
|
||||
private onPathClicked;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { PluginCmdHandler, PluginCommandInvoker } from "../connection/PluginCmdHandler";
|
||||
import { Registry } from "../events";
|
||||
import { PlayerStatus } from "../video-viewer/Definitions";
|
||||
import { Registry } from "tc-events";
|
||||
import { PluginCmdHandler, PluginCommandInvoker } from "tc-shared/connection/PluginCmdHandler";
|
||||
export interface W2GEvents {
|
||||
notify_watcher_add: {
|
||||
watcher: W2GWatcher;
|
|
@ -0,0 +1,6 @@
|
|||
/// <reference types="react" />
|
||||
export declare const Arrow: (props: {
|
||||
direction: NavigationReason;
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
}) => JSX.Element;
|
|
@ -6,6 +6,7 @@ export interface CheckboxProperties {
|
|||
onChange?: (value: boolean) => void;
|
||||
value?: boolean;
|
||||
initialValue?: boolean;
|
||||
className?: string;
|
||||
children?: never;
|
||||
}
|
||||
export interface CheckboxState {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="react" />
|
||||
export declare const CountryIcon: (props: {
|
||||
country: string;
|
||||
export declare const CountryCode: (props: {
|
||||
alphaCode: string;
|
||||
className?: string;
|
||||
}) => JSX.Element;
|
|
@ -1,5 +1,5 @@
|
|||
/// <reference types="react" />
|
||||
import { RemoteIcon } from "tc-shared/file/Icons";
|
||||
import * as React from "react";
|
||||
import { RemoteIcon, RemoteIconInfo } from "tc-shared/file/Icons";
|
||||
export declare const IconRenderer: (props: {
|
||||
icon: string;
|
||||
title?: string;
|
||||
|
@ -10,3 +10,8 @@ export declare const RemoteIconRenderer: (props: {
|
|||
className?: string;
|
||||
title?: string;
|
||||
}) => JSX.Element;
|
||||
export declare const RemoteIconInfoRenderer: React.MemoExoticComponent<(props: {
|
||||
icon: RemoteIconInfo;
|
||||
className?: string;
|
||||
title?: string;
|
||||
}) => JSX.Element>;
|
||||
|
|
|
@ -26,3 +26,4 @@ export declare const IconTooltip: (props: {
|
|||
className?: string;
|
||||
outerClassName?: string;
|
||||
}) => JSX.Element;
|
||||
export declare const TooltipHook: React.MemoExoticComponent<() => JSX.Element>;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { IpcRegistryDescription, Registry } from "tc-shared/events";
|
||||
import { VideoViewerEvents } from "tc-shared/video-viewer/Definitions";
|
||||
import { ChannelEditEvents } from "tc-shared/ui/modal/channel-edit/Definitions";
|
||||
import { EchoTestEvents } from "tc-shared/ui/modal/echo-test/Definitions";
|
||||
import { ModalGlobalSettingsEditorEvents } from "tc-shared/ui/modal/global-settings-editor/Definitions";
|
||||
|
@ -9,6 +8,14 @@ import { IpcVariableDescriptor } from "tc-shared/ui/utils/IpcVariable";
|
|||
import { ModalBookmarkEvents, ModalBookmarkVariables } from "tc-shared/ui/modal/bookmarks/Definitions";
|
||||
import { ModalBookmarksAddServerEvents, ModalBookmarksAddServerVariables } from "tc-shared/ui/modal/bookmarks-add-server/Definitions";
|
||||
import { ModalPokeEvents, ModalPokeVariables } from "tc-shared/ui/modal/poke/Definitions";
|
||||
import { ModalClientGroupAssignmentEvents, ModalClientGroupAssignmentVariables } from "tc-shared/ui/modal/group-assignment/Definitions";
|
||||
import { VideoViewerEvents } from "tc-shared/ui/modal/video-viewer/Definitions";
|
||||
import { PermissionModalEvents } from "tc-shared/ui/modal/permission/ModalDefinitions";
|
||||
import { PermissionEditorEvents } from "tc-shared/ui/modal/permission/EditorDefinitions";
|
||||
import { PermissionEditorServerInfo } from "tc-shared/ui/modal/permission/ModalRenderer";
|
||||
import { ModalAvatarUploadEvents, ModalAvatarUploadVariables } from "tc-shared/ui/modal/avatar-upload/Definitions";
|
||||
import { ModalInputProcessorEvents, ModalInputProcessorVariables } from "tc-shared/ui/modal/input-processor/Definitios";
|
||||
import { ModalAboutVariables } from "tc-shared/ui/modal/about/Definitions";
|
||||
export declare type ModalType = "error" | "warning" | "info" | "none";
|
||||
export declare type ModalRenderType = "page" | "dialog";
|
||||
export interface ModalOptions {
|
||||
|
@ -71,6 +78,8 @@ export interface ModalInstanceController {
|
|||
getEvents(): Registry<ModalInstanceEvents>;
|
||||
show(): Promise<void>;
|
||||
hide(): Promise<void>;
|
||||
minimize(): Promise<void>;
|
||||
maximize(): Promise<void>;
|
||||
destroy(): any;
|
||||
}
|
||||
export interface ModalController {
|
||||
|
@ -92,6 +101,7 @@ export declare abstract class AbstractModal {
|
|||
type(): ModalType;
|
||||
color(): "none" | "blue";
|
||||
verticalAlignment(): "top" | "center" | "bottom";
|
||||
/** @deprecated */
|
||||
protected onInitialize(): void;
|
||||
protected onDestroy(): void;
|
||||
protected onClose(): void;
|
||||
|
@ -114,4 +124,9 @@ export interface ModalConstructorArguments {
|
|||
"modal-bookmarks": [IpcRegistryDescription<ModalBookmarkEvents>, IpcVariableDescriptor<ModalBookmarkVariables>];
|
||||
"modal-bookmark-add-server": [IpcRegistryDescription<ModalBookmarksAddServerEvents>, IpcVariableDescriptor<ModalBookmarksAddServerVariables>];
|
||||
"modal-poked": [IpcRegistryDescription<ModalPokeEvents>, IpcVariableDescriptor<ModalPokeVariables>];
|
||||
"modal-assign-server-groups": [IpcRegistryDescription<ModalClientGroupAssignmentEvents>, IpcVariableDescriptor<ModalClientGroupAssignmentVariables>];
|
||||
"modal-permission-edit": [PermissionEditorServerInfo, IpcRegistryDescription<PermissionModalEvents>, IpcRegistryDescription<PermissionEditorEvents>];
|
||||
"modal-avatar-upload": [IpcRegistryDescription<ModalAvatarUploadEvents>, IpcVariableDescriptor<ModalAvatarUploadVariables>, string];
|
||||
"modal-input-processor": [IpcRegistryDescription<ModalInputProcessorEvents>, IpcVariableDescriptor<ModalInputProcessorVariables>];
|
||||
"modal-about": [IpcRegistryDescription, IpcVariableDescriptor<ModalAboutVariables>];
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@ export declare class PageModalRenderer extends React.PureComponent<{
|
|||
modalInstance: AbstractModal;
|
||||
onBackdropClicked: () => void;
|
||||
children: React.ReactElement<ModalFrameRenderer>;
|
||||
}, {
|
||||
shown: boolean;
|
||||
}> {
|
||||
constructor(props: any);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Registry } from "tc-events";
|
||||
import { ModalOptions } from "tc-shared/ui/react-elements/modal/Definitions";
|
||||
import { ModalInstanceController, ModalInstanceEvents, ModalState } from "tc-shared/ui/react-elements/modal/Definitions";
|
||||
import { ModalInstanceController, ModalInstanceEvents, ModalOptions, ModalState } from "tc-shared/ui/react-elements/modal/Definitions";
|
||||
export declare class ExternalModalController implements ModalInstanceController {
|
||||
private readonly modalType;
|
||||
private readonly modalOptions;
|
||||
|
@ -10,6 +9,7 @@ export declare class ExternalModalController implements ModalInstanceController
|
|||
private ipcRemotePeerId;
|
||||
private ipcChannel;
|
||||
private readonly modalEvents;
|
||||
private modalInitialized;
|
||||
private modalInitializeCallback;
|
||||
private windowId;
|
||||
private windowListener;
|
||||
|
@ -20,6 +20,8 @@ export declare class ExternalModalController implements ModalInstanceController
|
|||
getState(): ModalState;
|
||||
show(): Promise<void>;
|
||||
hide(): Promise<void>;
|
||||
minimize(): Promise<void>;
|
||||
maximize(): Promise<void>;
|
||||
private mutateWindow;
|
||||
private handleWindowDestroyed;
|
||||
private registerIpcMessageHandler;
|
||||
|
|
|
@ -1,25 +1,40 @@
|
|||
import { ModalInstanceController, ModalInstanceEvents, ModalOptions, ModalState } from "tc-shared/ui/react-elements/modal/Definitions";
|
||||
import { AbstractModal, ModalInstanceController, ModalInstanceEvents, ModalOptions, ModalState } from "tc-shared/ui/react-elements/modal/Definitions";
|
||||
import * as React from "react";
|
||||
import { RegisteredModal } from "tc-shared/ui/react-elements/modal/Registry";
|
||||
import { Registry } from "tc-events";
|
||||
declare class InternalRendererInstance extends React.PureComponent<{
|
||||
instance: InternalModalInstance;
|
||||
}, {
|
||||
shown: boolean;
|
||||
}> {
|
||||
constructor(props: any);
|
||||
render(): JSX.Element;
|
||||
componentWillUnmount(): void;
|
||||
}
|
||||
export declare class InternalModalInstance implements ModalInstanceController {
|
||||
readonly instanceUniqueId: string;
|
||||
readonly events: Registry<ModalInstanceEvents>;
|
||||
readonly refRendererInstance: React.RefObject<InternalRendererInstance>;
|
||||
private readonly modalKlass;
|
||||
private readonly constructorArguments;
|
||||
private readonly rendererInstance;
|
||||
private readonly modalOptions;
|
||||
private state;
|
||||
private modalInstance;
|
||||
private htmlContainer;
|
||||
modalInstance: AbstractModal;
|
||||
private modalInitializePromise;
|
||||
constructor(modalType: RegisteredModal<any>, constructorArguments: any[], modalOptions: ModalOptions);
|
||||
private constructModal;
|
||||
private destructModal;
|
||||
private destructModalInstance;
|
||||
getState(): ModalState;
|
||||
getEvents(): Registry<ModalInstanceEvents>;
|
||||
show(): Promise<void>;
|
||||
hide(): Promise<void>;
|
||||
minimize(): Promise<void>;
|
||||
maximize(): Promise<void>;
|
||||
destroy(): void;
|
||||
private getCloseCallback;
|
||||
private getPopoutCallback;
|
||||
private getMinimizeCallback;
|
||||
getCloseCallback(): () => void;
|
||||
getPopoutCallback(): () => void;
|
||||
getMinimizeCallback(): any;
|
||||
}
|
||||
export declare const InternalModalHook: React.MemoExoticComponent<() => JSX.Element>;
|
||||
export {};
|
||||
|
|
|
@ -209,9 +209,6 @@ export interface ChannelTreeUIEvents {
|
|||
treeEntryId: number;
|
||||
unread: boolean;
|
||||
};
|
||||
notify_visibility_changed: {
|
||||
visible: boolean;
|
||||
};
|
||||
notify_destroy: {};
|
||||
}
|
||||
export declare type ChannelTreeDragEntry = {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import * as React from "react";
|
||||
declare type EntryTagStyle = "text-only" | "normal";
|
||||
export declare const ServerTag: React.MemoExoticComponent<(props: {
|
||||
serverName: string;
|
||||
handlerId: string;
|
||||
serverUniqueId?: string;
|
||||
className?: string;
|
||||
style?: EntryTagStyle;
|
||||
}) => JSX.Element>;
|
||||
export declare const ClientTag: React.MemoExoticComponent<(props: {
|
||||
clientName: string;
|
||||
|
@ -12,6 +14,7 @@ export declare const ClientTag: React.MemoExoticComponent<(props: {
|
|||
clientId?: number;
|
||||
clientDatabaseId?: number;
|
||||
className?: string;
|
||||
style?: EntryTagStyle;
|
||||
}) => JSX.Element>;
|
||||
export declare const ChannelTag: React.MemoExoticComponent<(props: {
|
||||
channelName: string;
|
||||
|
@ -19,3 +22,4 @@ export declare const ChannelTag: React.MemoExoticComponent<(props: {
|
|||
handlerId: string;
|
||||
className?: string;
|
||||
}) => JSX.Element>;
|
||||
export {};
|
||||
|
|
|
@ -32,7 +32,6 @@ export declare class ChannelTreeView extends ReactComponentBase<ChannelTreeViewP
|
|||
constructor(props: any);
|
||||
componentDidMount(): void;
|
||||
componentWillUnmount(): void;
|
||||
private handleVisibilityChanged;
|
||||
private visibleEntries;
|
||||
render(): JSX.Element;
|
||||
private onScroll;
|
||||
|
|
|
@ -20,6 +20,7 @@ export declare abstract class UiVariableProvider<Variables extends UiVariableMap
|
|||
getArtificialDelay(): number;
|
||||
setArtificialDelay(value: number): void;
|
||||
setVariableProvider<T extends keyof Variables>(variable: T, provider: (customData: any) => Variables[T] | Promise<Variables[T]>): void;
|
||||
setVariableProviderAsync<T extends keyof Variables>(variable: T, provider: (customData: any) => Promise<Variables[T]>): void;
|
||||
/**
|
||||
* @param variable
|
||||
* @param editor If the editor returns `false` or a new variable, such variable will be used
|
||||
|
|
|
@ -2,7 +2,10 @@ import { ChangeLog } from "../update/ChangeLog";
|
|||
export interface Updater {
|
||||
getChangeLog(): ChangeLog;
|
||||
getChangeList(oldVersion: string): ChangeLog;
|
||||
getLastUsedVersion(): string;
|
||||
/**
|
||||
* @returns `undefined` if `updateUsedVersion()` never has been called.
|
||||
*/
|
||||
getLastUsedVersion(): string | undefined;
|
||||
getCurrentVersion(): string;
|
||||
updateUsedVersion(): any;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,112 @@ export declare enum FilterMode {
|
|||
*/
|
||||
Block = 2
|
||||
}
|
||||
/**
|
||||
* All available options for input processing.
|
||||
* Since input processing is only available on the native client these are the options
|
||||
* the native client (especially WebRTC audio processing) have.
|
||||
*/
|
||||
export interface InputProcessorConfigWebRTC {
|
||||
"pipeline.maximum_internal_processing_rate": number;
|
||||
"pipeline.multi_channel_render": boolean;
|
||||
"pipeline.multi_channel_capture": boolean;
|
||||
"pre_amplifier.enabled": boolean;
|
||||
"pre_amplifier.fixed_gain_factor": number;
|
||||
"high_pass_filter.enabled": boolean;
|
||||
"high_pass_filter.apply_in_full_band": boolean;
|
||||
"echo_canceller.enabled": boolean;
|
||||
"echo_canceller.mobile_mode": boolean;
|
||||
"echo_canceller.export_linear_aec_output": boolean;
|
||||
"echo_canceller.enforce_high_pass_filtering": boolean;
|
||||
"noise_suppression.enabled": boolean;
|
||||
"noise_suppression.level": "low" | "moderate" | "high" | "very-high";
|
||||
"noise_suppression.analyze_linear_aec_output_when_available": boolean;
|
||||
"transient_suppression.enabled": boolean;
|
||||
"voice_detection.enabled": boolean;
|
||||
"gain_controller1.enabled": boolean;
|
||||
"gain_controller1.mode": "adaptive-analog" | "adaptive-digital" | "fixed-digital";
|
||||
"gain_controller1.target_level_dbfs": number;
|
||||
"gain_controller1.compression_gain_db": number;
|
||||
"gain_controller1.enable_limiter": boolean;
|
||||
"gain_controller1.analog_level_minimum": number;
|
||||
"gain_controller1.analog_level_maximum": number;
|
||||
"gain_controller1.analog_gain_controller.enabled": boolean;
|
||||
"gain_controller1.analog_gain_controller.startup_min_volume": number;
|
||||
"gain_controller1.analog_gain_controller.clipped_level_min": number;
|
||||
"gain_controller1.analog_gain_controller.enable_agc2_level_estimator": boolean;
|
||||
"gain_controller1.analog_gain_controller.enable_digital_adaptive": boolean;
|
||||
"gain_controller2.enabled": boolean;
|
||||
"gain_controller2.fixed_digital.gain_db": number;
|
||||
"gain_controller2.adaptive_digital.enabled": boolean;
|
||||
"gain_controller2.adaptive_digital.vad_probability_attack": number;
|
||||
"gain_controller2.adaptive_digital.level_estimator": "rms" | "peak";
|
||||
"gain_controller2.adaptive_digital.level_estimator_adjacent_speech_frames_threshold": number;
|
||||
"gain_controller2.adaptive_digital.use_saturation_protector": boolean;
|
||||
"gain_controller2.adaptive_digital.initial_saturation_margin_db": number;
|
||||
"gain_controller2.adaptive_digital.extra_saturation_margin_db": number;
|
||||
"gain_controller2.adaptive_digital.gain_applier_adjacent_speech_frames_threshold": number;
|
||||
"gain_controller2.adaptive_digital.max_gain_change_db_per_second": number;
|
||||
"gain_controller2.adaptive_digital.max_output_noise_level_dbfs": number;
|
||||
"residual_echo_detector.enabled": boolean;
|
||||
"level_estimation.enabled": boolean;
|
||||
}
|
||||
/**
|
||||
* Attention:
|
||||
* These keys **MUST** be equal to all keys of `InputProcessorConfigWebRTC`.
|
||||
* All keys not registered in here will not be consideration.
|
||||
*/
|
||||
export declare const kInputProcessorConfigWebRTCKeys: (keyof InputProcessorConfigWebRTC)[];
|
||||
export interface InputProcessorConfigRNNoise {
|
||||
"rnnoise.enabled": boolean;
|
||||
}
|
||||
/**
|
||||
* Attention:
|
||||
* These keys **MUST** be equal to all keys of `InputProcessorConfigWebRTC`.
|
||||
* All keys not registered in here will not be consideration.
|
||||
*/
|
||||
export declare const kInputProcessorConfigRNNoiseKeys: (keyof InputProcessorConfigRNNoise)[];
|
||||
export interface InputProcessorConfigMapping {
|
||||
"webrtc-processing": InputProcessorConfigWebRTC;
|
||||
"rnnoise": InputProcessorConfigRNNoise;
|
||||
}
|
||||
export declare type InputProcessorType = keyof InputProcessorConfigMapping;
|
||||
export interface InputProcessorStatistics {
|
||||
output_rms_dbfs: number | undefined;
|
||||
voice_detected: number | undefined;
|
||||
echo_return_loss: number | undefined;
|
||||
echo_return_loss_enhancement: number | undefined;
|
||||
divergent_filter_fraction: number | undefined;
|
||||
delay_median_ms: number | undefined;
|
||||
delay_standard_deviation_ms: number | undefined;
|
||||
residual_echo_likelihood: number | undefined;
|
||||
residual_echo_likelihood_recent_max: number | undefined;
|
||||
delay_ms: number | undefined;
|
||||
rnnoise_volume: number | undefined;
|
||||
}
|
||||
export interface InputProcessor {
|
||||
/**
|
||||
* @param processor Target processor type
|
||||
* @returns `true` if the target processor type is supported and available
|
||||
*/
|
||||
hasProcessor(processor: InputProcessorType): boolean;
|
||||
/**
|
||||
* Get the processor config of the target type.
|
||||
* This method will throw when the target processor isn't supported.
|
||||
* @param processor Target processor type.
|
||||
* @returns The processor config.
|
||||
*/
|
||||
getProcessorConfig<T extends InputProcessorType>(processor: T): InputProcessorConfigMapping[T];
|
||||
/**
|
||||
* Apply the target config.
|
||||
* @param processor
|
||||
* @param config
|
||||
*/
|
||||
applyProcessorConfig<T extends InputProcessorType>(processor: T, config: InputProcessorConfigMapping[T]): any;
|
||||
/**
|
||||
* Get the current processor statistics.
|
||||
*/
|
||||
getStatistics(): InputProcessorStatistics;
|
||||
}
|
||||
export interface AbstractInput {
|
||||
readonly events: Registry<InputEvents>;
|
||||
currentState(): InputState;
|
||||
|
@ -89,6 +195,13 @@ export interface AbstractInput {
|
|||
removeFilter(filter: Filter): any;
|
||||
getVolume(): number;
|
||||
setVolume(volume: number): any;
|
||||
getInputProcessor(): InputProcessor;
|
||||
/**
|
||||
* Create a new level meter for this audio input.
|
||||
* This level meter will be indicate the audio level after all processing.
|
||||
* Note: Changing the input device or stopping the input will result in no activity.
|
||||
*/
|
||||
createLevelMeter(): LevelMeter;
|
||||
}
|
||||
export interface LevelMeter {
|
||||
getDevice(): InputDevice;
|
||||
|
|
|
@ -29,6 +29,27 @@ export declare const defaultRecorderEvents: Registry<DefaultRecorderEvents>;
|
|||
export declare function setDefaultRecorder(recorder: RecorderProfile): void;
|
||||
export interface RecorderProfileEvents {
|
||||
notify_device_changed: {};
|
||||
notify_voice_start: {};
|
||||
notify_voice_end: {};
|
||||
notify_input_initialized: {};
|
||||
}
|
||||
export declare abstract class RecorderProfileOwner {
|
||||
/**
|
||||
* This method will be called from the recorder profile.
|
||||
*/
|
||||
protected abstract handleUnmount(): any;
|
||||
/**
|
||||
* This callback will be called when the recorder audio input has
|
||||
* been initialized.
|
||||
* Note: This method might be called within ownRecorder().
|
||||
* If this method has been called, handleUnmount will be called.
|
||||
*
|
||||
* @param input The target input.
|
||||
*/
|
||||
protected abstract handleRecorderInput(input: AbstractInput): any;
|
||||
}
|
||||
export declare abstract class ConnectionRecorderProfileOwner extends RecorderProfileOwner {
|
||||
abstract getConnection(): ConnectionHandler;
|
||||
}
|
||||
export declare class RecorderProfile {
|
||||
readonly events: Registry<RecorderProfileEvents>;
|
||||
|
@ -36,11 +57,9 @@ export declare class RecorderProfile {
|
|||
readonly volatile: any;
|
||||
config: RecorderProfileConfig;
|
||||
input: AbstractInput;
|
||||
private currentOwner;
|
||||
private currentOwnerMutex;
|
||||
current_handler: ConnectionHandler;
|
||||
callback_input_initialized: (input: AbstractInput) => void;
|
||||
callback_start: () => any;
|
||||
callback_stop: () => any;
|
||||
callback_unmount: () => any;
|
||||
private readonly pptHook;
|
||||
private pptTimeout;
|
||||
private pptHookRegistered;
|
||||
|
@ -52,6 +71,13 @@ export declare class RecorderProfile {
|
|||
private save;
|
||||
private reinitializePPTHook;
|
||||
private reinitializeFilter;
|
||||
/**
|
||||
* Own the recorder.
|
||||
*/
|
||||
ownRecorder(target: RecorderProfileOwner | undefined): Promise<void>;
|
||||
getOwner(): RecorderProfileOwner | undefined;
|
||||
isInputActive(): boolean;
|
||||
/** @deprecated use `ownRecorder(undefined)` */
|
||||
unmount(): Promise<void>;
|
||||
getVadType(): VadType;
|
||||
setVadType(type: VadType): boolean;
|
||||
|
|
|
@ -123,5 +123,5 @@ function deploy_client() {
|
|||
#install_npm
|
||||
#compile_scripts
|
||||
#compile_native
|
||||
package_client
|
||||
#package_client
|
||||
deploy_client
|
||||
|
|
|
@ -39,24 +39,11 @@ export function initialize() {
|
|||
window.displayCriticalError = _impl;
|
||||
}
|
||||
|
||||
loader.register_task(loader.Stage.JAVASCRIPT, {
|
||||
name: "teaclient jquery",
|
||||
loader.register_task(Stage.JAVASCRIPT, {
|
||||
name: "handler initialize #2",
|
||||
priority: -1,
|
||||
function: async () => {
|
||||
window.$ = require("jquery");
|
||||
|
||||
window.jQuery = window.$;
|
||||
Object.assign(window.$, window.jsrender = require('jsrender'));
|
||||
},
|
||||
priority: 80
|
||||
});
|
||||
|
||||
loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "handler initialize",
|
||||
priority: 80,
|
||||
function: async () => {
|
||||
await import("../renderer/Logger");
|
||||
await import("../renderer/PersistentLocalStorage");
|
||||
await import("../renderer/ContextMenu");
|
||||
await import("../renderer/hooks/StorageAdapter");
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
import * as electron from "electron";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs-extra";
|
||||
import {StorageAdapter} from "tc-shared/StorageAdapter";
|
||||
import {ServerSettingsStorage} from "tc-shared/ServerSettings";
|
||||
import {LogCategory, logError} from "tc-shared/log";
|
||||
|
||||
const kStoragePath = path.join(electron.remote.app.getPath("userData"), "settings");
|
||||
|
||||
function storageKeyPath(key: string) {
|
||||
return path.join(kStoragePath, encodeURIComponent(key));
|
||||
}
|
||||
|
||||
export class ClientStorageAdapter implements StorageAdapter {
|
||||
delete(key: string): Promise<void> {
|
||||
return fs.remove(storageKeyPath(key)).catch(error => {});
|
||||
}
|
||||
|
||||
async get(key: string): Promise<string | null> {
|
||||
try {
|
||||
const result = await fs.readFile(storageKeyPath(key));
|
||||
return JSON.parse(result.toString());
|
||||
} catch (error) {
|
||||
logError(LogCategory.GENERAL, tr("Failed to load client storage key %s: %o"), key, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
has(key: string): Promise<boolean> {
|
||||
return fs.pathExists(storageKeyPath(key));
|
||||
}
|
||||
|
||||
set(key: string, value: string): Promise<void> {
|
||||
return fs.writeFile(storageKeyPath(key), JSON.stringify(value)).catch(error => {
|
||||
logError(LogCategory.GENERAL, tr("Failed to set client storage key %s: %o"), key, error);
|
||||
});
|
||||
}
|
||||
}
|
||||
export let clientStorage;
|
||||
|
||||
export class ClientServerSettingsStorage implements ServerSettingsStorage {
|
||||
get(serverUniqueId: string): string {
|
||||
try {
|
||||
const result = fs.readFileSync(storageKeyPath("settings.server_" + serverUniqueId));
|
||||
return JSON.parse(result.toString());
|
||||
} catch (error) {
|
||||
logError(LogCategory.GENERAL, tr("Failed to load individual server settings for %s: %o"), serverUniqueId, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
set(serverUniqueId: string, value: string) {
|
||||
try {
|
||||
fs.writeFileSync(storageKeyPath("settings.server_" + serverUniqueId), JSON.stringify(value));
|
||||
} catch (error) {
|
||||
logError(LogCategory.GENERAL, tr("Failed to write individual server settings for %s: %o"), serverUniqueId, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function initializeClientStorage() {
|
||||
await fs.mkdirp(kStoragePath);
|
||||
clientStorage = new ClientStorageAdapter();
|
||||
}
|
|
@ -3,13 +3,15 @@ import * as path from "path";
|
|||
import * as fs from "fs-extra";
|
||||
import {Updater} from "tc-shared/update/Updater";
|
||||
import {ChangeLog, ChangeSetEntry} from "tc-shared/update/ChangeLog";
|
||||
import {settings, Settings} from "tc-shared/settings";
|
||||
|
||||
function getChangeLogFile() {
|
||||
const app_path = electron.remote.app.getAppPath();
|
||||
if(app_path.endsWith(".asar"))
|
||||
if(app_path.endsWith(".asar")) {
|
||||
return path.join(path.dirname(app_path), "..", "ChangeLog.txt");
|
||||
else
|
||||
return path.join(app_path, "github", "ChangeLog.txt"); /* We've the source master :D */
|
||||
} else {
|
||||
return path.join(app_path, "github", "ChangeLog.txt"); /* We've the source :D */
|
||||
}
|
||||
}
|
||||
|
||||
const EntryRegex = /^([0-9]+)\.([0-9]+)\.([0-9]+)(-b[0-9]+)?:$/m;
|
||||
|
@ -31,8 +33,9 @@ function parseChangeLogEntry(lines: string[], index: number) : { entries: Change
|
|||
if(trimmed[0] === '-') {
|
||||
const depth = lines[index].indexOf('-');
|
||||
if(depth > entryDepth) {
|
||||
if(typeof currentEntry === "undefined")
|
||||
if(typeof currentEntry === "undefined") {
|
||||
throw "missing change child entries parent at line " + index;
|
||||
}
|
||||
|
||||
const result = parseChangeLogEntry(lines, index);
|
||||
entries.push({
|
||||
|
@ -45,8 +48,9 @@ function parseChangeLogEntry(lines: string[], index: number) : { entries: Change
|
|||
break;
|
||||
} else {
|
||||
/* new entry */
|
||||
if(typeof currentEntry === "string")
|
||||
if(typeof currentEntry === "string") {
|
||||
entries.push(currentEntry);
|
||||
}
|
||||
|
||||
currentEntry = trimmed.substr(1).trim();
|
||||
}
|
||||
|
@ -57,8 +61,9 @@ function parseChangeLogEntry(lines: string[], index: number) : { entries: Change
|
|||
index++;
|
||||
}
|
||||
|
||||
if(typeof currentEntry === "string")
|
||||
if(typeof currentEntry === "string") {
|
||||
entries.push(currentEntry);
|
||||
}
|
||||
|
||||
return {
|
||||
index: index,
|
||||
|
@ -75,8 +80,9 @@ async function parseClientChangeLog() : Promise<ChangeLog> {
|
|||
const lines = (await fs.readFile(getChangeLogFile())).toString("UTF-8").split("\n");
|
||||
let index = 0;
|
||||
|
||||
while(index < lines.length && !lines[index].match(EntryRegex))
|
||||
while(index < lines.length && !lines[index].match(EntryRegex)) {
|
||||
index++;
|
||||
}
|
||||
|
||||
while(index < lines.length) {
|
||||
const [ _, major, minor, patch, build ] = lines[index].match(EntryRegex);
|
||||
|
@ -93,7 +99,6 @@ async function parseClientChangeLog() : Promise<ChangeLog> {
|
|||
return result;
|
||||
}
|
||||
|
||||
const kLastUsedVersionKey = "updater-used-version-native";
|
||||
export class ClientUpdater implements Updater {
|
||||
private changeLog: ChangeLog;
|
||||
private currentVersion: string;
|
||||
|
@ -131,10 +136,10 @@ export class ClientUpdater implements Updater {
|
|||
}
|
||||
|
||||
getLastUsedVersion(): string {
|
||||
return localStorage.getItem(kLastUsedVersionKey) || "1.4.9";
|
||||
return settings.getValue(Settings.KEY_UPDATER_LAST_USED_CLIENT, undefined);
|
||||
}
|
||||
|
||||
updateUsedVersion() {
|
||||
localStorage.setItem(kLastUsedVersionKey, this.getCurrentVersion());
|
||||
settings.setValue(Settings.KEY_UPDATER_LAST_USED_CLIENT, this.getCurrentVersion());
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import {ContextMenuEntry, ContextMenuFactory, setGlobalContextMenuFactory} from "tc-shared/ui/ContextMenu";
|
||||
import {ContextMenuEntry, ContextMenuFactory} from "tc-shared/ui/ContextMenu";
|
||||
import * as electron from "electron";
|
||||
import {MenuItemConstructorOptions} from "electron";
|
||||
import {clientIconClassToImage, remoteIconDatafier, RemoteIconWrapper} from "./IconHelper";
|
||||
|
@ -109,7 +109,7 @@ class ContextMenuInstance {
|
|||
}
|
||||
}
|
||||
|
||||
setGlobalContextMenuFactory(new class implements ContextMenuFactory {
|
||||
export class ClientContextMenuFactory implements ContextMenuFactory {
|
||||
closeContextMenu() {
|
||||
currentMenu?.destroy();
|
||||
currentMenu = undefined;
|
||||
|
@ -120,4 +120,4 @@ setGlobalContextMenuFactory(new class implements ContextMenuFactory {
|
|||
currentMenu = new ContextMenuInstance(entries, callbackClose);
|
||||
currentMenu.spawn(position.pageX, position.pageY);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -37,7 +37,7 @@ function create_logger(name: string) : Logger {
|
|||
const log = (type, message: string, ...args) => {
|
||||
switch (type) {
|
||||
case LogType.TRACE:
|
||||
original_console.trace(message, ...args);
|
||||
original_console.debug(message, ...args);
|
||||
break;
|
||||
case LogType.DEBUG:
|
||||
original_console.debug(message, ...args);
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
import * as electron from "electron";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs-extra";
|
||||
|
||||
const APP_DATA = electron.remote.app.getPath("userData");
|
||||
const SETTINGS_DIR = path.join(APP_DATA, "settings");
|
||||
|
||||
let _local_storage: {[key: string]: any} = {};
|
||||
let _local_storage_save: {[key: string]: boolean} = {};
|
||||
let _save_timer: number;
|
||||
|
||||
export async function initialize() {
|
||||
await fs.mkdirp(SETTINGS_DIR);
|
||||
|
||||
console.error("Load local storage from: %o", SETTINGS_DIR);
|
||||
const files = await fs.readdir(SETTINGS_DIR);
|
||||
for(const file of files) {
|
||||
const key = decodeURIComponent(file);
|
||||
console.log("Load settings: %s", key);
|
||||
|
||||
try {
|
||||
const data = await fs.readFile(path.join(SETTINGS_DIR, file));
|
||||
_local_storage[key] = JSON.parse(data.toString() || "{}");
|
||||
} catch(error) {
|
||||
const target_file = path.join(SETTINGS_DIR, file + "." + Date.now() + ".broken");
|
||||
console.error("Failed to load settings for %s: %o. Moving settings so the file does not get overridden. Target file: %s", key, error, target_file);
|
||||
try {
|
||||
await fs.move(path.join(SETTINGS_DIR, file), target_file);
|
||||
} catch (error) {
|
||||
console.warn("Failed to move broken settings file!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _new_storage: Storage = {} as any;
|
||||
|
||||
_new_storage.getItem = key => _local_storage[key] || null;
|
||||
_new_storage.setItem = (key, value) => {
|
||||
_local_storage[key] = value;
|
||||
_local_storage_save[key] = true;
|
||||
(_new_storage as any)["length"] = Object.keys(_local_storage).length;
|
||||
};
|
||||
|
||||
_new_storage.clear = () => {
|
||||
_local_storage = {};
|
||||
_local_storage_save = {};
|
||||
|
||||
try {
|
||||
fs.emptyDirSync(SETTINGS_DIR);
|
||||
} catch(error) {
|
||||
console.warn("Failed to empty settings dir");
|
||||
}
|
||||
(_new_storage as any)["length"] = 0;
|
||||
};
|
||||
|
||||
_new_storage.key = index => Object.keys(_local_storage)[index];
|
||||
_new_storage.removeItem = key => {
|
||||
delete _local_storage[key];
|
||||
delete_key(key).catch(error => {
|
||||
console.warn("Failed to delete key on fs: %s => %o", key, error);
|
||||
});
|
||||
(_new_storage as any)["length"] = Object.keys(_local_storage).length;
|
||||
};
|
||||
Object.assign(window.localStorage, _new_storage);
|
||||
|
||||
/* try to save everything all 60 seconds */
|
||||
_save_timer = setInterval(() => {
|
||||
save_all_sync();
|
||||
}, 60 * 1000);
|
||||
}
|
||||
|
||||
export function save_all_sync() {
|
||||
for(const key of Object.keys(_local_storage_save))
|
||||
save_key_sync(key);
|
||||
}
|
||||
|
||||
function key_path(key: string) {
|
||||
return path.join(SETTINGS_DIR, encodeURIComponent(key));
|
||||
}
|
||||
|
||||
export function save_key_sync(key: string) {
|
||||
if(!_local_storage_save[key])
|
||||
return;
|
||||
|
||||
delete _local_storage_save[key];
|
||||
const setting_path = key_path(key);
|
||||
fs.writeJsonSync(setting_path, _local_storage[key], {spaces: 0});
|
||||
}
|
||||
|
||||
export async function delete_key(key: string) {
|
||||
delete _local_storage_save[key];
|
||||
const setting_path = key_path(key);
|
||||
await fs.remove(setting_path); /* could be async because we're not carrying about data */
|
||||
}
|
||||
|
||||
window.addEventListener("beforeunload", () => {
|
||||
console.log("Save local storage");
|
||||
save_all_sync();
|
||||
});
|
|
@ -25,6 +25,10 @@ export class NativeWindowManager implements WindowManager {
|
|||
constructor() {
|
||||
this.windowInstances = {};
|
||||
this.events = new Registry<WindowManagerEvents>();
|
||||
|
||||
window.onunload = () => {
|
||||
Object.values(this.windowInstances).forEach(window => window.destroy());
|
||||
};
|
||||
}
|
||||
|
||||
getEvents(): Registry<WindowManagerEvents> {
|
||||
|
|
|
@ -4,8 +4,14 @@ import {
|
|||
InputConsumer,
|
||||
InputConsumerType,
|
||||
InputEvents,
|
||||
InputProcessor,
|
||||
InputProcessorConfigMapping,
|
||||
InputProcessorStatistics,
|
||||
InputProcessorType,
|
||||
InputStartError,
|
||||
InputState,
|
||||
kInputProcessorConfigRNNoiseKeys,
|
||||
kInputProcessorConfigWebRTCKeys,
|
||||
LevelMeter,
|
||||
} from "tc-shared/voice/RecorderBase";
|
||||
import {audio} from "tc-native/connection";
|
||||
|
@ -17,15 +23,16 @@ import {getRecorderBackend, InputDevice} from "tc-shared/audio/Recorder";
|
|||
import {LogCategory, logError, logTrace, logWarn} from "tc-shared/log";
|
||||
import {Settings, settings} from "tc-shared/settings";
|
||||
import NativeFilterMode = audio.record.FilterMode;
|
||||
import AudioProcessor = audio.record.AudioProcessor;
|
||||
|
||||
export class NativeInput implements AbstractInput {
|
||||
static readonly instances = [] as NativeInput[];
|
||||
|
||||
readonly events: Registry<InputEvents>;
|
||||
|
||||
readonly nativeHandle: audio.record.AudioRecorder;
|
||||
readonly nativeConsumer: audio.record.AudioConsumer;
|
||||
private readonly inputProcessor: NativeInputProcessor;
|
||||
private readonly nativeHandle: audio.record.AudioRecorder;
|
||||
private readonly nativeConsumer: audio.record.AudioConsumer;
|
||||
|
||||
private listenerRNNoise: () => void;
|
||||
private state: InputState;
|
||||
private deviceId: string | undefined;
|
||||
|
||||
|
@ -36,9 +43,11 @@ export class NativeInput implements AbstractInput {
|
|||
this.events = new Registry<InputEvents>();
|
||||
|
||||
this.nativeHandle = audio.record.create_recorder();
|
||||
this.inputProcessor = new NativeInputProcessor(this.nativeHandle.get_audio_processor());
|
||||
this.inputProcessor.applyProcessorConfig("rnnoise", { "rnnoise.enabled": settings.getValue(Settings.KEY_RNNOISE_FILTER) });
|
||||
this.listenerRNNoise = settings.globalChangeListener(Settings.KEY_RNNOISE_FILTER, newValue => this.inputProcessor.applyProcessorConfig("rnnoise", { "rnnoise.enabled": newValue }));
|
||||
|
||||
this.nativeConsumer = this.nativeHandle.create_consumer();
|
||||
this.nativeConsumer.toggle_rnnoise(settings.getValue(Settings.KEY_RNNOISE_FILTER));
|
||||
|
||||
this.nativeConsumer.callback_ended = () => {
|
||||
this.filtered = true;
|
||||
|
@ -50,13 +59,12 @@ export class NativeInput implements AbstractInput {
|
|||
};
|
||||
|
||||
this.state = InputState.PAUSED;
|
||||
NativeInput.instances.push(this);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
const index = NativeInput.instances.indexOf(this);
|
||||
if(index !== -1) {
|
||||
NativeInput.instances.splice(index, 1);
|
||||
if(this.listenerRNNoise) {
|
||||
this.listenerRNNoise();
|
||||
this.listenerRNNoise = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,43 +275,145 @@ export class NativeInput implements AbstractInput {
|
|||
this.nativeConsumer.set_filter_mode(nativeMode);
|
||||
this.events.fire("notify_filter_mode_changed", { oldMode, newMode: mode });
|
||||
}
|
||||
|
||||
getInputProcessor(): InputProcessor {
|
||||
return this.inputProcessor;
|
||||
}
|
||||
|
||||
createLevelMeter(): LevelMeter {
|
||||
return new NativeInputLevelMeter(this.nativeHandle.create_level_meter("post-process"));
|
||||
}
|
||||
}
|
||||
|
||||
class NativeInputProcessor implements InputProcessor {
|
||||
private readonly processor: AudioProcessor;
|
||||
|
||||
constructor(processor: AudioProcessor) {
|
||||
this.processor = processor;
|
||||
}
|
||||
|
||||
applyProcessorConfig<T extends InputProcessorType>(processor: T, config: Partial<InputProcessorConfigMapping[T]>) {
|
||||
let keys: string[];
|
||||
switch (processor) {
|
||||
case "webrtc-processing":
|
||||
keys = kInputProcessorConfigWebRTCKeys;
|
||||
break;
|
||||
|
||||
case "rnnoise":
|
||||
keys = kInputProcessorConfigRNNoiseKeys;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw "invalid processor";
|
||||
}
|
||||
|
||||
const filteredConfig = {};
|
||||
keys.forEach(key => {
|
||||
if(typeof config[key] === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
filteredConfig[key] = config[key];
|
||||
});
|
||||
this.processor.apply_config(filteredConfig);
|
||||
}
|
||||
|
||||
getProcessorConfig<T extends InputProcessorType>(processor: T): InputProcessorConfigMapping[T] {
|
||||
let keys: string[];
|
||||
const config = this.processor.get_config();
|
||||
|
||||
switch (processor) {
|
||||
case "webrtc-processing":
|
||||
keys = kInputProcessorConfigWebRTCKeys;
|
||||
break;
|
||||
|
||||
case "rnnoise":
|
||||
keys = kInputProcessorConfigRNNoiseKeys;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw "invalid processor";
|
||||
}
|
||||
|
||||
const result = {};
|
||||
keys.forEach(key => result[key] = config[key]);
|
||||
return result as any;
|
||||
}
|
||||
|
||||
getStatistics(): InputProcessorStatistics {
|
||||
return this.processor.get_statistics();
|
||||
}
|
||||
|
||||
hasProcessor(processor: InputProcessorType): boolean {
|
||||
switch (processor) {
|
||||
case "rnnoise":
|
||||
case "webrtc-processing":
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NativeInputLevelMeter implements LevelMeter {
|
||||
private nativeHandle: audio.record.AudioLevelMeter;
|
||||
|
||||
constructor(nativeHandle: audio.record.AudioLevelMeter) {
|
||||
this.nativeHandle = nativeHandle;
|
||||
this.nativeHandle.start(error => {
|
||||
if(typeof error !== "undefined") {
|
||||
logError(LogCategory.AUDIO, tr("Native input audio level meter failed to start. This should not happen. Reason: %o"), error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
destroy(): any {
|
||||
this.nativeHandle?.set_callback(undefined);
|
||||
this.nativeHandle?.stop();
|
||||
this.nativeHandle = undefined;
|
||||
}
|
||||
|
||||
getDevice(): InputDevice {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
setObserver(callback: (value: number) => any) {
|
||||
this.nativeHandle.set_callback(level => {
|
||||
try {
|
||||
callback(level);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class NativeLevelMeter implements LevelMeter {
|
||||
static readonly instances: NativeLevelMeter[] = [];
|
||||
readonly targetDevice: InputDevice;
|
||||
|
||||
public nativeRecorder: audio.record.AudioRecorder;
|
||||
public nativeConsumer: audio.record.AudioConsumer;
|
||||
|
||||
private callback: (num: number) => any;
|
||||
private nativeFilter: audio.record.ThresholdConsumeFilter;
|
||||
private nativeHandle: audio.record.AudioLevelMeter;
|
||||
|
||||
constructor(device: InputDevice) {
|
||||
this.targetDevice = device;
|
||||
this.callback = () => {};
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
try {
|
||||
this.nativeRecorder = audio.record.create_recorder();
|
||||
this.nativeConsumer = this.nativeRecorder.create_consumer();
|
||||
|
||||
this.nativeConsumer.toggle_rnnoise(settings.getValue(Settings.KEY_RNNOISE_FILTER));
|
||||
|
||||
this.nativeFilter = this.nativeConsumer.create_filter_threshold(.5);
|
||||
this.nativeFilter.set_attack_smooth(.75);
|
||||
this.nativeFilter.set_release_smooth(.75);
|
||||
|
||||
await new Promise(resolve => this.nativeRecorder.set_device(this.targetDevice.deviceId, resolve));
|
||||
this.nativeHandle = audio.record.create_device_level_meter(this.targetDevice.deviceId);
|
||||
await new Promise((resolve, reject) => {
|
||||
this.nativeRecorder.start(flag => {
|
||||
if (typeof flag === "boolean" && flag)
|
||||
resolve();
|
||||
else
|
||||
reject(typeof flag === "string" ? flag : "failed to start");
|
||||
this.nativeHandle.start(error => {
|
||||
if(typeof error !== "undefined") {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* TODO: May implement smoothing to the native level meter as well? */
|
||||
//this.nativeFilter.set_attack_smooth(.75);
|
||||
//this.nativeFilter.set_release_smooth(.75);
|
||||
} catch (error) {
|
||||
if (typeof (error) === "string") {
|
||||
throw error;
|
||||
|
@ -312,40 +422,25 @@ export class NativeLevelMeter implements LevelMeter {
|
|||
logWarn(LogCategory.AUDIO, tr("Failed to initialize level meter for device %o: %o"), this.targetDevice, error);
|
||||
throw "initialize failed (lookup console)";
|
||||
}
|
||||
|
||||
/* references this variable, needs a destroy() call, else memory leak */
|
||||
this.nativeFilter.set_analyze_filter(value => this.callback(value));
|
||||
NativeLevelMeter.instances.push(this);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
const index = NativeLevelMeter.instances.indexOf(this);
|
||||
if(index !== -1) {
|
||||
NativeLevelMeter.instances.splice(index, 1);
|
||||
}
|
||||
|
||||
if (this.nativeFilter) {
|
||||
this.nativeFilter.set_analyze_filter(undefined);
|
||||
this.nativeConsumer.unregister_filter(this.nativeFilter);
|
||||
}
|
||||
|
||||
if (this.nativeConsumer) {
|
||||
this.nativeRecorder.delete_consumer(this.nativeConsumer);
|
||||
}
|
||||
|
||||
if(this.nativeRecorder) {
|
||||
this.nativeRecorder.stop();
|
||||
}
|
||||
this.nativeRecorder = undefined;
|
||||
this.nativeConsumer = undefined;
|
||||
this.nativeFilter = undefined;
|
||||
this.nativeHandle?.set_callback(undefined);
|
||||
this.nativeHandle?.stop();
|
||||
this.nativeHandle = undefined;
|
||||
}
|
||||
|
||||
getDevice(): InputDevice {
|
||||
return this.targetDevice;
|
||||
}
|
||||
|
||||
setObserver(callback: (value: number) => any) {
|
||||
this.callback = callback || (() => {});
|
||||
setObserver(callback: (value: number) => void) {
|
||||
this.nativeHandle.set_callback(level => {
|
||||
try {
|
||||
callback(level);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -171,7 +171,7 @@ class ErrorCommandHandler extends AbstractCommandHandler {
|
|||
connection_bandwidth_received_last_minute_speech: 0,
|
||||
connection_bandwidth_received_last_minute_keepalive: 0,
|
||||
connection_bandwidth_received_last_minute_control: 0
|
||||
}
|
||||
}, { process_result: false }
|
||||
);
|
||||
}
|
||||
return false;
|
||||
|
@ -211,9 +211,23 @@ export class ServerConnection extends AbstractServerConnection {
|
|||
|
||||
this.nativeHandle = spawn_native_server_connection();
|
||||
this.nativeHandle.callback_disconnect = reason => {
|
||||
this.client.handleDisconnect(DisconnectReason.CONNECTION_CLOSED, {
|
||||
reason: reason
|
||||
});
|
||||
switch (this.connectionState) {
|
||||
case ConnectionState.CONNECTING:
|
||||
case ConnectionState.AUTHENTICATING:
|
||||
case ConnectionState.INITIALISING:
|
||||
this.client.handleDisconnect(DisconnectReason.CONNECT_FAILURE, reason);
|
||||
break;
|
||||
|
||||
case ConnectionState.CONNECTED:
|
||||
this.client.handleDisconnect(DisconnectReason.CONNECTION_CLOSED, {
|
||||
reason: reason
|
||||
});
|
||||
break;
|
||||
|
||||
case ConnectionState.DISCONNECTING:
|
||||
case ConnectionState.UNCONNECTED:
|
||||
break;
|
||||
}
|
||||
};
|
||||
this.nativeHandle.callback_command = (command, args, switches) => {
|
||||
console.log("Received: %o %o %o", command, args, switches);
|
||||
|
|
|
@ -3,18 +3,20 @@ import {
|
|||
VoiceConnectionStatus,
|
||||
WhisperSessionInitializer
|
||||
} from "tc-shared/connection/VoiceConnection";
|
||||
import {RecorderProfile} from "tc-shared/voice/RecorderProfile";
|
||||
import {ConnectionRecorderProfileOwner, RecorderProfile} from "tc-shared/voice/RecorderProfile";
|
||||
import {NativeServerConnection, NativeVoiceClient, NativeVoiceConnection, PlayerState} from "tc-native/connection";
|
||||
import {ServerConnection} from "./ServerConnection";
|
||||
import {VoiceClient} from "tc-shared/voice/VoiceClient";
|
||||
import {WhisperSession, WhisperTarget} from "tc-shared/voice/VoiceWhisper";
|
||||
import {NativeInput} from "../audio/AudioRecorder";
|
||||
import {ConnectionState} from "tc-shared/ConnectionHandler";
|
||||
import {ConnectionHandler, ConnectionState} from "tc-shared/ConnectionHandler";
|
||||
import {VoicePlayerEvents, VoicePlayerLatencySettings, VoicePlayerState} from "tc-shared/voice/VoicePlayer";
|
||||
import {Registry} from "tc-shared/events";
|
||||
import {LogCategory, logDebug, logInfo, logWarn} from "tc-shared/log";
|
||||
import {LogCategory, logError, logInfo, logWarn} from "tc-shared/log";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {ConnectionStatistics} from "tc-shared/connection/ConnectionBase";
|
||||
import {AbstractInput} from "tc-shared/voice/RecorderBase";
|
||||
import {crashOnThrow, ignorePromise} from "tc-shared/proto";
|
||||
|
||||
export class NativeVoiceConnectionWrapper extends AbstractVoiceConnection {
|
||||
private readonly serverConnectionStateChangedListener;
|
||||
|
@ -24,6 +26,9 @@ export class NativeVoiceConnectionWrapper extends AbstractVoiceConnection {
|
|||
private connectionState: VoiceConnectionStatus;
|
||||
private currentRecorder: RecorderProfile;
|
||||
|
||||
private ignoreRecorderUnmount: boolean;
|
||||
private listenerRecorder: (() => void)[];
|
||||
|
||||
private registeredVoiceClients: {[key: number]: NativeVoiceClientWrapper} = {};
|
||||
|
||||
private currentlyReplayingAudio = false;
|
||||
|
@ -32,6 +37,7 @@ export class NativeVoiceConnectionWrapper extends AbstractVoiceConnection {
|
|||
constructor(connection: ServerConnection, voice: NativeVoiceConnection) {
|
||||
super(connection);
|
||||
this.native = voice;
|
||||
this.ignoreRecorderUnmount = false;
|
||||
|
||||
this.serverConnectionStateChangedListener = () => {
|
||||
if(this.connection.getConnectionState() === ConnectionState.CONNECTED) {
|
||||
|
@ -78,46 +84,61 @@ export class NativeVoiceConnectionWrapper extends AbstractVoiceConnection {
|
|||
return this.native.decoding_supported(codec);
|
||||
}
|
||||
|
||||
async acquireVoiceRecorder(recorder: RecorderProfile | undefined): Promise<void> {
|
||||
if(this.currentRecorder === recorder) {
|
||||
async acquireVoiceRecorder(recorder: RecorderProfile | undefined, enforce?: boolean): Promise<void> {
|
||||
if(this.currentRecorder === recorder && !enforce) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.currentRecorder) {
|
||||
this.currentRecorder.callback_unmount = undefined;
|
||||
this.native.set_audio_source(undefined);
|
||||
this.listenerRecorder?.forEach(callback => callback());
|
||||
this.listenerRecorder = undefined;
|
||||
|
||||
this.handleVoiceEndEvent();
|
||||
await this.currentRecorder.unmount();
|
||||
this.currentRecorder = undefined;
|
||||
if(this.currentRecorder) {
|
||||
this.ignoreRecorderUnmount = true;
|
||||
this.ignoreRecorderUnmount = false;
|
||||
|
||||
this.native.set_audio_source(undefined);
|
||||
}
|
||||
|
||||
await recorder?.unmount();
|
||||
const oldRecorder = recorder;
|
||||
this.currentRecorder = recorder;
|
||||
|
||||
try {
|
||||
if(recorder) {
|
||||
if(!(recorder.input instanceof NativeInput)) {
|
||||
this.currentRecorder = undefined;
|
||||
throw "Recorder input must be an instance of NativeInput!";
|
||||
if(this.currentRecorder) {
|
||||
const connection = this;
|
||||
await recorder.ownRecorder(new class extends ConnectionRecorderProfileOwner {
|
||||
getConnection(): ConnectionHandler {
|
||||
return connection.connection.client;
|
||||
}
|
||||
|
||||
recorder.current_handler = this.connection.client;
|
||||
recorder.callback_unmount = () => {
|
||||
logDebug(LogCategory.VOICE, tr("Lost voice recorder..."));
|
||||
this.acquireVoiceRecorder(undefined);
|
||||
};
|
||||
protected handleRecorderInput(input: AbstractInput): any {
|
||||
if(!(input instanceof NativeInput)) {
|
||||
logError(LogCategory.VOICE, tr("Recorder input isn't an instance of NativeInput. Ignoring recorder input."));
|
||||
return;
|
||||
}
|
||||
|
||||
recorder.callback_start = this.handleVoiceStartEvent.bind(this);
|
||||
recorder.callback_stop = this.handleVoiceEndEvent.bind(this);
|
||||
connection.native.set_audio_source(input.getNativeConsumer());
|
||||
}
|
||||
|
||||
this.native.set_audio_source(recorder.input.getNativeConsumer());
|
||||
}
|
||||
} catch(error) {
|
||||
this.currentRecorder = undefined;
|
||||
throw error;
|
||||
protected handleUnmount(): any {
|
||||
if(connection.ignoreRecorderUnmount) {
|
||||
return;
|
||||
}
|
||||
|
||||
connection.currentRecorder = undefined;
|
||||
ignorePromise(crashOnThrow(connection.acquireVoiceRecorder(undefined, true)));
|
||||
}
|
||||
});
|
||||
|
||||
this.listenerRecorder = [];
|
||||
this.listenerRecorder.push(recorder.events.on("notify_voice_start", () => this.handleVoiceStartEvent()));
|
||||
this.listenerRecorder.push(recorder.events.on("notify_voice_end", () => this.handleVoiceEndEvent(tr("recorder event"))));
|
||||
}
|
||||
|
||||
if(this.currentRecorder?.isInputActive()) {
|
||||
this.handleVoiceStartEvent();
|
||||
} else {
|
||||
this.handleVoiceEndEvent(tr("recorder change"));
|
||||
}
|
||||
|
||||
this.events.fire("notify_recorder_changed", {
|
||||
oldRecorder,
|
||||
newRecorder: recorder
|
||||
|
@ -144,6 +165,7 @@ export class NativeVoiceConnectionWrapper extends AbstractVoiceConnection {
|
|||
if(status === this.currentlyReplayingAudio) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentlyReplayingAudio = status;
|
||||
this.events.fire("notify_voice_replay_state_change", { replaying: status });
|
||||
}
|
||||
|
@ -161,26 +183,22 @@ export class NativeVoiceConnectionWrapper extends AbstractVoiceConnection {
|
|||
|
||||
this.native.enable_voice_send(true);
|
||||
this.localAudioStarted = true;
|
||||
logInfo(LogCategory.VOICE, tr("Local voice started"));
|
||||
|
||||
const ch = chandler.getClient();
|
||||
if(ch) ch.speaking = true;
|
||||
logInfo(LogCategory.VOICE, tr("Local voice started"));
|
||||
chandler.getClient()?.setSpeaking(true);
|
||||
}
|
||||
|
||||
private handleVoiceEndEvent() {
|
||||
private handleVoiceEndEvent(reason: string) {
|
||||
this.native.enable_voice_send(false);
|
||||
|
||||
if(!this.localAudioStarted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chandler = this.connection.client;
|
||||
const ch = chandler.getClient();
|
||||
if(ch) ch.speaking = false;
|
||||
chandler.getClient()?.setSpeaking(false);
|
||||
|
||||
if(!chandler.connected)
|
||||
return false;
|
||||
|
||||
if(chandler.isMicrophoneMuted())
|
||||
return false;
|
||||
|
||||
logInfo(LogCategory.VOICE, tr("Local voice ended"));
|
||||
logInfo(LogCategory.VOICE, tr("Local voice ended (%s)"), reason);
|
||||
this.localAudioStarted = false;
|
||||
}
|
||||
|
||||
|
@ -196,12 +214,15 @@ export class NativeVoiceConnectionWrapper extends AbstractVoiceConnection {
|
|||
}
|
||||
|
||||
unregisterVoiceClient(client: VoiceClient) {
|
||||
if(!(client instanceof NativeVoiceClientWrapper))
|
||||
if(!(client instanceof NativeVoiceClientWrapper)) {
|
||||
throw "invalid client type";
|
||||
}
|
||||
|
||||
delete this.registeredVoiceClients[client.getClientId()];
|
||||
this.native.unregister_client(client.getClientId());
|
||||
client.destroy();
|
||||
|
||||
this.handleVoiceClientStateChange();
|
||||
}
|
||||
|
||||
stopAllVoiceReplays() {
|
||||
|
@ -221,18 +242,15 @@ export class NativeVoiceConnectionWrapper extends AbstractVoiceConnection {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
setWhisperSessionInitializer(initializer: WhisperSessionInitializer | undefined) {
|
||||
}
|
||||
setWhisperSessionInitializer(initializer: WhisperSessionInitializer | undefined) { }
|
||||
|
||||
startWhisper(target: WhisperTarget): Promise<void> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
dropWhisperSession(session: WhisperSession) {
|
||||
}
|
||||
dropWhisperSession(session: WhisperSession) { }
|
||||
|
||||
stopWhisper() {
|
||||
}
|
||||
stopWhisper() { }
|
||||
|
||||
getConnectionStats(): Promise<ConnectionStatistics> {
|
||||
/* FIXME: This is iffy! */
|
||||
|
@ -276,6 +294,10 @@ class NativeVoiceClientWrapper implements VoiceClient {
|
|||
case PlayerState.STOPPING:
|
||||
this.setState(VoicePlayerState.STOPPING);
|
||||
break;
|
||||
|
||||
default:
|
||||
logError(LogCategory.VOICE, tr("Native audio player has invalid state: %o"), state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,13 +17,4 @@ setRecorderBackend(new class implements AudioRecorderBacked {
|
|||
getDeviceList(): DeviceList {
|
||||
return inputDeviceList;
|
||||
}
|
||||
|
||||
isRnNoiseSupported(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
toggleRnNoise(target: boolean) {
|
||||
NativeLevelMeter.instances.forEach(input => input.nativeConsumer.toggle_rnnoise(target));
|
||||
NativeInput.instances.forEach(input => input.nativeConsumer.toggle_rnnoise(target));
|
||||
}
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
import * as loader from "tc-loader";
|
||||
import {setGlobalContextMenuFactory} from "tc-shared/ui/ContextMenu";
|
||||
import {ClientContextMenuFactory} from "../ContextMenu";
|
||||
|
||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "context menu",
|
||||
function: async () => {
|
||||
setGlobalContextMenuFactory(new ClientContextMenuFactory());
|
||||
},
|
||||
priority: 60
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import {ClientServerSettingsStorage} from "../ClientStorage";
|
||||
import {setServerSettingsStorage} from "tc-shared/ServerSettings";
|
||||
|
||||
loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "server storage init",
|
||||
function: async () => setServerSettingsStorage(new ClientServerSettingsStorage()),
|
||||
priority: 80
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
import {setStorageAdapter} from "tc-shared/StorageAdapter";
|
||||
import {clientStorage, initializeClientStorage} from "../ClientStorage";
|
||||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
|
||||
loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
|
||||
name: "storage init",
|
||||
function: async () => {
|
||||
await initializeClientStorage();
|
||||
setStorageAdapter(clientStorage);
|
||||
},
|
||||
|
||||
/* Must come before everything else! */
|
||||
priority: 10_000
|
||||
});
|
|
@ -1,9 +1,12 @@
|
|||
import "./StorageAdapter";
|
||||
import "./ContextMenu";
|
||||
import "./AudioInput";
|
||||
import "./AudioBackend";
|
||||
import "./Backend";
|
||||
import "./ChangeLogClient";
|
||||
import "./Dns";
|
||||
import "./MenuBar";
|
||||
import "./ServerSettingsAdapter";
|
||||
import "./ServerConnection";
|
||||
import "./Video";
|
||||
import "./Sound";
|
||||
|
|
|
@ -41,26 +41,6 @@ declare global {
|
|||
}
|
||||
}
|
||||
|
||||
/* we use out own jquery resource */
|
||||
loader.register_task(loader.Stage.JAVASCRIPT, {
|
||||
name: "teaclient jquery",
|
||||
function: async () => {
|
||||
window.$ = require("jquery");
|
||||
window.jQuery = window.$;
|
||||
Object.assign(window.$, window.jsrender = require('jsrender'));
|
||||
},
|
||||
priority: 80
|
||||
});
|
||||
|
||||
loader.register_task(loader.Stage.SETUP, {
|
||||
name: "teaclient initialize persistent storage",
|
||||
function: async () => {
|
||||
const storage = require("./PersistentLocalStorage");
|
||||
await storage.initialize();
|
||||
},
|
||||
priority: 90
|
||||
});
|
||||
|
||||
loader.register_task(loader.Stage.INITIALIZING, {
|
||||
name: "teaclient initialize logging",
|
||||
function: async () => {
|
||||
|
@ -149,19 +129,16 @@ loader.register_task(loader.Stage.LOADED, {
|
|||
});
|
||||
|
||||
|
||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||
loader.register_task(loader.Stage.JAVASCRIPT, {
|
||||
name: "teaclient load adapters",
|
||||
function: async () => {
|
||||
/* all files which replaces a native driver */
|
||||
try {
|
||||
await import("./MenuBar");
|
||||
await import("./ContextMenu");
|
||||
await import("./hooks");
|
||||
await import("./SingleInstanceHandler");
|
||||
await import("./IconHelper");
|
||||
await import("./connection/FileTransfer");
|
||||
|
||||
await import("./hooks");
|
||||
|
||||
await import("./UnloadHandler");
|
||||
await import("./WindowsTrayHandler");
|
||||
} catch (error) {
|
||||
|
@ -171,5 +148,6 @@ loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
|||
}
|
||||
remote.getCurrentWindow().on('focus', () => remote.getCurrentWindow().flashFrame(false));
|
||||
},
|
||||
priority: 60
|
||||
/* Register all tasks after all javascript files have been loaded */
|
||||
priority: -1
|
||||
});
|
|
@ -11,7 +11,8 @@
|
|||
"tc-loader": ["imports/loader"],
|
||||
"svg-sprites/*": ["imports/svg-sprites/*"],
|
||||
"tc-events": ["imports/vendor/TeaEventBus/src/index.d.ts"],
|
||||
"tc-services": ["imports/vendor/TeaClientServices/src/index.d.ts"]
|
||||
"tc-services": ["imports/vendor/TeaClientServices/src/index.d.ts"],
|
||||
"tc-native/connection": ["native/serverconnection/exports/exports.d.ts"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
"tc-loader": ["imports/loader"],
|
||||
"svg-sprites/*": ["imports/svg-sprites/*"],
|
||||
"tc-events": ["imports/vendor/TeaEventBus/src/index.d.ts"],
|
||||
"tc-services": ["imports/vendor/TeaClientServices/src/index.d.ts"]
|
||||
"tc-services": ["imports/vendor/TeaClientServices/src/index.d.ts"],
|
||||
"tc-native/connection": ["native/serverconnection/exports/exports.d.ts"]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
|
|
Loading…
Reference in New Issue