TeaWeb/shared/js/events.ts

605 lines
18 KiB
TypeScript
Raw Normal View History

2020-03-30 11:44:18 +00:00
//TODO: Combine EventConvert and Event?
import {MusicClientEntry, SongInfo} from "tc-shared/ui/client";
import {PlaylistSong} from "tc-shared/connection/ServerConnectionDeclaration";
import {guid} from "tc-shared/crypto/uid";
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
export interface EventConvert<All> {
as<T extends keyof All>() : All[T];
}
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
export interface Event<T> {
readonly type: T;
}
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
export class SingletonEvent implements Event<"singletone-instance"> {
static readonly instance = new SingletonEvent();
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
readonly type = "singletone-instance";
private constructor() { }
}
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
export class Registry<Events> {
private readonly registry_uuid;
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
private handler: {[key: string]: ((event) => void)[]} = {};
private connections: {[key: string]:Registry<string>[]} = {};
private debug_prefix = undefined;
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
constructor() {
this.registry_uuid = "evreg_data_" + guid();
}
2020-03-27 15:15:15 +00:00
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
enable_debug(prefix: string) { this.debug_prefix = prefix || "---"; }
disable_debug() { this.debug_prefix = undefined; }
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
on<T extends keyof Events>(event: T, handler: (event?: Events[T] & Event<T> & EventConvert<Events>) => void);
on(events: (keyof Events)[], handler: (event?: Event<keyof Events> & EventConvert<Events>) => void);
on(events, handler) {
if(!Array.isArray(events))
events = [events];
handler[this.registry_uuid] = {
singleshot: false
};
for(const event of events) {
const handlers = this.handler[event] || (this.handler[event] = []);
handlers.push(handler);
2020-03-27 15:15:15 +00:00
}
2020-03-30 11:44:18 +00:00
}
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
/* one */
one<T extends keyof Events>(event: T, handler: (event?: Events[T] & Event<T> & EventConvert<Events>) => void);
one(events: (keyof Events)[], handler: (event?: Event<keyof Events> & EventConvert<Events>) => void);
one(events, handler) {
if(!Array.isArray(events))
events = [events];
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
for(const event of events) {
const handlers = this.handler[event] || (this.handler[event] = []);
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
handler[this.registry_uuid] = { singleshot: true };
handlers.push(handler);
2020-02-02 14:05:36 +00:00
}
2020-03-30 11:44:18 +00:00
}
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
off<T extends keyof Events>(handler: (event?: Event<T>) => void);
off<T extends keyof Events>(event: T, handler: (event?: Event<T> & EventConvert<Events>) => void);
off(event: (keyof Events)[], handler: (event?: Event<keyof Events> & EventConvert<Events>) => void);
off(handler_or_events, handler?) {
if(typeof handler_or_events === "function") {
for(const key of Object.keys(this.handler))
this.handler[key].remove(handler_or_events);
} else {
if(!Array.isArray(handler_or_events))
handler_or_events = [handler_or_events];
for(const event of handler_or_events) {
const handlers = this.handler[event];
if(handlers) handlers.remove(handler);
2020-02-02 14:05:36 +00:00
}
}
2020-03-30 11:44:18 +00:00
}
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
connect<EOther, T extends keyof Events & keyof EOther>(event: T, target: Registry<EOther>) {
(this.connections[event as string] || (this.connections[event as string] = [])).push(target as any);
}
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
disconnect<EOther, T extends keyof Events & keyof EOther>(event: T, target: Registry<EOther>) {
(this.connections[event as string] || []).remove(target as any);
}
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
disconnect_all<EOther>(target: Registry<EOther>) {
for(const event of Object.keys(this.connections))
this.connections[event].remove(target as any);
}
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
fire<T extends keyof Events>(event_type: T, data?: Events[T]) {
if(this.debug_prefix) console.log("[%s] Trigger event: %s", this.debug_prefix, event_type);
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
const event = Object.assign(typeof data === "undefined" ? SingletonEvent.instance : data, {
type: event_type,
as: function () { return this; }
});
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
for(const handler of (this.handler[event_type as string] || [])) {
handler(event);
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
const reg_data = handler[this.registry_uuid];
if(typeof reg_data === "object" && reg_data.singleshot)
this.handler[event_type as string].remove(handler);
2020-02-02 14:05:36 +00:00
}
2020-03-30 11:44:18 +00:00
for(const evhandler of (this.connections[event_type as string] || []))
evhandler.fire(event_type as any, event as any);
}
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
fire_async<T extends keyof Events>(event_type: T, data?: Events[T]) {
setTimeout(() => this.fire(event_type, data));
2020-02-02 14:05:36 +00:00
}
2019-08-21 08:00:01 +00:00
2020-03-30 11:44:18 +00:00
destory() {
this.handler = {};
}
}
2019-08-21 08:00:01 +00:00
2020-03-30 11:44:18 +00:00
export namespace channel_tree {
export interface client {
"enter_view": {},
"left_view": {},
"property_update": {
properties: string[]
},
"music_status_update": {
player_buffered_index: number,
player_replay_index: number
},
"music_song_change": {
"song": SongInfo
},
/* TODO: Move this out of the music bots interface? */
"playlist_song_add": { song: PlaylistSong },
"playlist_song_remove": { song_id: number },
"playlist_song_reorder": { song_id: number, previous_song_id: number },
"playlist_song_loaded": { song_id: number, success: boolean, error_msg?: string, metadata?: string },
2019-08-21 08:00:01 +00:00
}
2020-03-30 11:44:18 +00:00
}
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
export namespace sidebar {
export interface music {
"open": {}, /* triggers when frame should be shown */
"close": {}, /* triggers when frame will be closed */
"bot_change": {
old: MusicClientEntry | undefined,
new: MusicClientEntry | undefined
},
"bot_property_update": {
properties: string[]
},
"action_play": {},
"action_pause": {},
"action_song_set": { song_id: number },
"action_forward": {},
"action_rewind": {},
"action_forward_ms": {
units: number;
},
"action_rewind_ms": {
units: number;
},
"action_song_add": {},
"action_song_delete": { song_id: number },
"action_playlist_reload": {},
"playtime_move_begin": {},
"playtime_move_end": {
canceled: boolean,
target_time?: number
},
"reorder_begin": { song_id: number; entry: JQuery },
"reorder_end": { song_id: number; canceled: boolean; entry: JQuery; previous_entry?: number },
"player_time_update": channel_tree.client["music_status_update"],
"player_song_change": channel_tree.client["music_song_change"],
"playlist_song_add": channel_tree.client["playlist_song_add"] & { insert_effect?: boolean },
"playlist_song_remove": channel_tree.client["playlist_song_remove"],
"playlist_song_reorder": channel_tree.client["playlist_song_reorder"],
"playlist_song_loaded": channel_tree.client["playlist_song_loaded"] & { html_entry?: JQuery },
}
}
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
export namespace modal {
export type BotStatusType = "name" | "description" | "volume" | "country_code" | "channel_commander" | "priority_speaker";
export type PlaylistStatusType = "replay_mode" | "finished" | "delete_played" | "max_size" | "notify_song_change";
export interface music_manage {
show_container: { container: "settings" | "permissions"; };
/* setting relevant */
query_bot_status: {},
bot_status: {
status: "success" | "error";
error_msg?: string;
data?: {
name: string,
description: string,
volume: number,
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
country_code: string,
default_country_code: string,
channel_commander: boolean,
priority_speaker: boolean,
client_version: string,
client_platform: string,
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
uptime_mode: number,
bot_type: number
}
},
set_bot_status: {
key: BotStatusType,
value: any
},
set_bot_status_result: {
key: BotStatusType,
status: "success" | "error" | "timeout",
error_msg?: string,
value?: any
2020-02-02 14:05:36 +00:00
}
2020-03-30 11:44:18 +00:00
query_playlist_status: {},
playlist_status: {
status: "success" | "error",
error_msg?: string,
data?: {
replay_mode: number,
finished: boolean,
delete_played: boolean,
max_size: number,
notify_song_change: boolean
}
},
set_playlist_status: {
key: PlaylistStatusType,
value: any
},
set_playlist_status_result: {
key: PlaylistStatusType,
status: "success" | "error" | "timeout",
error_msg?: string,
value?: any
}
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
/* permission relevant */
show_client_list: {},
hide_client_list: {},
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
filter_client_list: { filter: string | undefined },
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
"refresh_permissions": {},
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
query_special_clients: {},
special_client_list: {
status: "success" | "error" | "error-permission",
error_msg?: string,
clients?: {
name: string,
unique_id: string,
database_id: number
}[]
},
search_client: { text: string },
search_client_result: {
status: "error" | "timeout" | "empty" | "success",
error_msg?: string,
client?: {
name: string,
unique_id: string,
database_id: number
}
},
2020-02-02 14:05:36 +00:00
2020-03-30 11:44:18 +00:00
/* sets a client to set the permission for */
special_client_set: {
client?: {
name: string,
unique_id: string,
database_id: number
}
},
"query_general_permissions": {},
"general_permissions": {
status: "error" | "timeout" | "success",
error_msg?: string,
permissions?: {[key: string]:number}
},
"set_general_permission_result": {
status: "error" | "success",
key: string,
value?: number,
error_msg?: string
},
"set_general_permission": { /* try to change a permission for the server */
key: string,
value: number
},
"query_client_permissions": { client_database_id: number },
"client_permissions": {
status: "error" | "timeout" | "success",
client_database_id: number,
error_msg?: string,
permissions?: {[key: string]:number}
},
"set_client_permission_result": {
status: "error" | "success",
client_database_id: number,
key: string,
value?: number,
error_msg?: string
},
"set_client_permission": { /* try to change a permission for the server */
client_database_id: number,
key: string,
value: number
},
"query_group_permissions": { permission_name: string },
"group_permissions": {
permission_name: string;
status: "error" | "timeout" | "success"
groups?: {
name: string,
value: number,
id: number
}[],
error_msg?: string
2020-02-02 14:05:36 +00:00
}
}
2020-03-30 11:44:18 +00:00
export interface newcomer {
"show_step": {
"step": "welcome" | "microphone" | "identity" | "finish"
},
"exit_guide": {
ask_yesno: boolean
},
2020-02-22 13:30:17 +00:00
2020-03-30 11:44:18 +00:00
"modal-shown": {},
2020-02-22 13:30:17 +00:00
2020-03-30 11:44:18 +00:00
"step-status": {
next_button: boolean,
previous_button: boolean
}
}
2020-02-22 13:30:17 +00:00
2020-03-30 11:44:18 +00:00
export namespace settings {
export type ProfileInfo = {
id: string,
name: string,
nickname: string,
identity_type: "teaforo" | "teamspeak" | "nickname",
2020-02-22 13:30:17 +00:00
2020-03-30 11:44:18 +00:00
identity_forum?: {
valid: boolean,
fallback_name: string
2020-02-22 13:30:17 +00:00
},
2020-03-30 11:44:18 +00:00
identity_nickname?: {
name: string,
fallback_name: string
2020-02-22 13:30:17 +00:00
},
2020-03-30 11:44:18 +00:00
identity_teamspeak?: {
unique_id: string,
fallback_name: string
2020-02-22 13:30:17 +00:00
}
2020-03-30 11:44:18 +00:00
}
2020-02-22 13:30:17 +00:00
2020-03-30 11:44:18 +00:00
export interface profiles {
"reload-profile": { profile_id?: string },
"select-profile": { profile_id: string },
2020-02-22 13:30:17 +00:00
2020-03-30 11:44:18 +00:00
"query-profile-list": { },
"query-profile-list-result": {
status: "error" | "success" | "timeout",
2020-02-22 13:30:17 +00:00
2020-03-30 11:44:18 +00:00
error?: string;
profiles?: ProfileInfo[]
}
2020-02-22 13:30:17 +00:00
2020-03-30 11:44:18 +00:00
"query-profile": { profile_id: string },
"query-profile-result": {
status: "error" | "success" | "timeout",
profile_id: string,
2020-02-22 13:30:17 +00:00
2020-03-30 11:44:18 +00:00
error?: string;
info?: ProfileInfo
2020-02-22 13:30:17 +00:00
},
2020-03-30 11:44:18 +00:00
"select-identity-type": {
profile_id: string,
identity_type: "teamspeak" | "teaforo" | "nickname" | "unset"
2020-02-22 13:30:17 +00:00
},
2020-03-30 11:44:18 +00:00
"query-profile-validity": { profile_id: string },
"query-profile-validity-result": {
profile_id: string,
status: "error" | "success" | "timeout",
2020-02-22 13:30:17 +00:00
2020-03-30 11:44:18 +00:00
error?: string,
valid?: boolean
2020-02-22 13:30:17 +00:00
}
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
"create-profile": { name: string },
"create-profile-result": {
status: "error" | "success" | "timeout",
name: string;
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
profile_id?: string;
error?: string;
},
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
"delete-profile": { profile_id: string },
"delete-profile-result": {
status: "error" | "success" | "timeout",
profile_id: string,
error?: string
2020-03-27 15:15:15 +00:00
}
2020-03-30 11:44:18 +00:00
"set-default-profile": { profile_id: string },
"set-default-profile-result": {
status: "error" | "success" | "timeout",
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
/* the profile which now has the id "default" */
old_profile_id: string,
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
/* the "default" profile which now has a new id */
new_profile_id?: string
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
error?: string;
}
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
/* profile name events */
"set-profile-name": {
profile_id: string,
name: string
},
"set-profile-name-result": {
status: "error" | "success" | "timeout",
profile_id: string,
name?: string
},
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
/* profile nickname events */
"set-default-name": {
profile_id: string,
name: string | null
},
"set-default-name-result": {
status: "error" | "success" | "timeout",
profile_id: string,
name?: string | null
},
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
"query-identity-teamspeak": { profile_id: string },
"query-identity-teamspeak-result": {
status: "error" | "success" | "timeout",
profile_id: string,
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
error?: string,
level?: number
}
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
"set-identity-name-name": { profile_id: string, name: string },
"set-identity-name-name-result": {
status: "error" | "success" | "timeout",
profile_id: string,
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
error?: string,
name?: string
},
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
"generate-identity-teamspeak": { profile_id: string },
"generate-identity-teamspeak-result": {
profile_id: string,
status: "error" | "success" | "timeout",
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
error?: string,
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
level?: number
unique_id?: string
},
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
"improve-identity-teamspeak-level": { profile_id: string },
"improve-identity-teamspeak-level-update": {
profile_id: string,
new_level: number
},
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
"import-identity-teamspeak": { profile_id: string },
"import-identity-teamspeak-result": {
profile_id: string,
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
level?: number
unique_id?: string
}
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
"export-identity-teamspeak": {
profile_id: string,
filename: string
},
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
"setup-forum-connection": {}
}
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
export type MicrophoneSettings = "volume" | "vad-type" | "ppt-key" | "ppt-release-delay" | "ppt-release-delay-active" | "threshold-threshold";
export interface microphone {
"query-devices": { refresh_list: boolean },
"query-device-result": {
status: "success" | "error" | "timeout",
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
error?: string,
devices?: {
id: string,
name: string,
driver: string
}[]
active_device?: string;
},
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
"query-settings": {},
"query-settings-result": {
status: "success" | "error" | "timeout",
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
error?: string,
info?: {
volume: number,
vad_type: string,
vad_ppt: {
key: any, /* ppt.KeyDescriptor */
release_delay: number,
release_delay_active: boolean
},
vad_threshold: {
threshold: number
}
2020-03-27 15:15:15 +00:00
}
2020-03-30 11:44:18 +00:00
},
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
"set-device": { device_id: string },
"set-device-result": {
device_id: string,
status: "success" | "error" | "timeout",
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
error?: string
},
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
"set-setting": {
setting: MicrophoneSettings;
value: any;
},
"set-setting-result": {
setting: MicrophoneSettings,
status: "success" | "error" | "timeout",
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
error?: string,
value?: any
},
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
"update-device-level": {
devices: {
2020-03-27 15:15:15 +00:00
device_id: string,
2020-03-30 11:44:18 +00:00
status: "success" | "error",
2020-03-27 15:15:15 +00:00
2020-03-30 11:44:18 +00:00
level?: number,
2020-03-27 15:15:15 +00:00
error?: string
2020-03-30 11:44:18 +00:00
}[]
},
"audio-initialized": {},
"deinitialize": {}
2020-03-27 15:15:15 +00:00
}
2020-02-22 13:30:17 +00:00
}
2020-02-02 14:05:36 +00:00
}
2020-03-30 11:44:18 +00:00
/*
//Some test code
2020-02-02 14:05:36 +00:00
const eclient = new events.Registry<events.channel_tree.client>();
const emusic = new events.Registry<events.sidebar.music>();
eclient.connect("playlist_song_loaded", emusic);
eclient.connect("playlist_song_loaded", emusic);
2020-03-30 11:44:18 +00:00
*/