From f18957e75e021a25c56738031a7ee2a2d8725b64 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Tue, 21 Apr 2020 16:17:21 +0200 Subject: [PATCH] Fixed a minor channel tree select bug. --- shared/js/ConnectionHandler.ts | 2 +- shared/js/connection/ConnectionBase.ts | 2 + shared/js/events.ts | 7 +- shared/js/main.tsx | 3 +- shared/js/ui/react-elements/Icon.tsx | 10 +-- .../ui/react-elements/ReactComponentBase.ts | 1 + shared/js/ui/tree/View.tsx | 11 +++ shared/js/ui/view.tsx | 70 +++++++++---------- 8 files changed, 59 insertions(+), 47 deletions(-) diff --git a/shared/js/ConnectionHandler.ts b/shared/js/ConnectionHandler.ts index d68e8930..8613bd2b 100644 --- a/shared/js/ConnectionHandler.ts +++ b/shared/js/ConnectionHandler.ts @@ -32,7 +32,6 @@ import * as dns from "tc-backend/dns"; import * as top_menu from "tc-shared/ui/frames/MenuBar"; import {EventHandler, Registry} from "tc-shared/events"; import {ServerLog} from "tc-shared/ui/frames/server_log"; -import {server} from "websocket"; export enum DisconnectReason { HANDLER_DESTROYED, @@ -639,6 +638,7 @@ export class ConnectionHandler { this.serverConnection.disconnect(); this.side_bar.private_conversations().clear_client_ids(); + this.side_bar.channel_conversations().set_current_channel(0); this.hostbanner.update(); if(auto_reconnect) { diff --git a/shared/js/connection/ConnectionBase.ts b/shared/js/connection/ConnectionBase.ts index b9e6a159..c18b4044 100644 --- a/shared/js/connection/ConnectionBase.ts +++ b/shared/js/connection/ConnectionBase.ts @@ -50,6 +50,8 @@ export abstract class AbstractServerConnection { //FIXME: Remove this this is currently only some kind of hack updateConnectionState(state: ConnectionState) { + if(state === this.connection_state_) return; + const old_state = this.connection_state_; this.connection_state_ = state; if(this.onconnectionstatechanged) diff --git a/shared/js/events.ts b/shared/js/events.ts index cd094a32..8efb7c89 100644 --- a/shared/js/events.ts +++ b/shared/js/events.ts @@ -1,5 +1,4 @@ import {ClientEvents, MusicClientEntry, SongInfo} from "tc-shared/ui/client"; -import {PlaylistSong} from "tc-shared/connection/ServerConnectionDeclaration"; import {guid} from "tc-shared/crypto/uid"; import * as React from "react"; @@ -232,7 +231,11 @@ export function ReactEventHandler, Event constructor.prototype.componentWillUnmount = function () { const registry = registry_callback(this); if(!registry) throw "Event registry returned for an event object is invalid"; - registry.unregister_handler(this); + try { + registry.unregister_handler(this); + } catch (error) { + console.warn("Failed to unregister event handler: %o", error); + } if(typeof willUnmount === "function") willUnmount.call(this, arguments); diff --git a/shared/js/main.tsx b/shared/js/main.tsx index 30895b87..11046d1e 100644 --- a/shared/js/main.tsx +++ b/shared/js/main.tsx @@ -29,8 +29,7 @@ import * as React from "react"; import * as ReactDOM from "react-dom"; import * as cbar from "./ui/frames/control-bar"; import * as global_ev_handler from "./events/ClientGlobalControlHandler"; -import {ClientGlobalControlEvents, global_client_actions} from "tc-shared/events/GlobalEvents"; -import {spawnSettingsModal} from "tc-shared/ui/modal/ModalSettings"; +import {global_client_actions} from "tc-shared/events/GlobalEvents"; /* required import for init */ require("./proto").initialize(); diff --git a/shared/js/ui/react-elements/Icon.tsx b/shared/js/ui/react-elements/Icon.tsx index 2e6c0068..7e360cfd 100644 --- a/shared/js/ui/react-elements/Icon.tsx +++ b/shared/js/ui/react-elements/Icon.tsx @@ -38,7 +38,9 @@ export class LocalIconRenderer extends React.Component { render() { const icon = this.props.icon; - if(icon.status === "loaded") { + if(!icon || icon.status === "empty" || icon.status === "destroyed") + return
; + else if(icon.status === "loaded") { if(icon.icon_id >= 0 && icon.icon_id <= 1000) { if(icon.icon_id === 0) return
; @@ -49,15 +51,13 @@ export class LocalIconRenderer extends React.Component { return
; else if(icon.status === "error") return
; - else if(icon.status === "empty" || icon.status === "destroyed") - return
; } componentDidMount(): void { - this.props.icon.status_change_callbacks.push(this.callback_state_update); + this.props.icon?.status_change_callbacks.push(this.callback_state_update); } componentWillUnmount(): void { - this.props.icon.status_change_callbacks.remove(this.callback_state_update); + this.props.icon?.status_change_callbacks.remove(this.callback_state_update); } } \ No newline at end of file diff --git a/shared/js/ui/react-elements/ReactComponentBase.ts b/shared/js/ui/react-elements/ReactComponentBase.ts index 4f89538e..636b79e1 100644 --- a/shared/js/ui/react-elements/ReactComponentBase.ts +++ b/shared/js/ui/react-elements/ReactComponentBase.ts @@ -28,6 +28,7 @@ let update_batches: {[key: number]:UpdateBatch} = { 0: generate_batch(), 1: generate_batch() }; +(window as any).update_batches = update_batches; export function BatchUpdateAssignment(type: BatchUpdateType) { return function (constructor: Function) { diff --git a/shared/js/ui/tree/View.tsx b/shared/js/ui/tree/View.tsx index 56929896..2e902f52 100644 --- a/shared/js/ui/tree/View.tsx +++ b/shared/js/ui/tree/View.tsx @@ -31,6 +31,8 @@ export interface ChannelTreeViewState { element_scroll_offset?: number; /* in px */ scroll_offset: number; /* in px */ view_height: number; /* in px */ + + tree_version: number; } type TreeEntry = ChannelEntry | ServerEntry | ClientEntry; @@ -67,6 +69,7 @@ export class ChannelTreeView extends ReactComponentBase("notify_tree_reset") + private handleTreeReset() { + this.rebuild_tree(); + this.setState({ + tree_version: this.state.tree_version + 1 + }); + } + private onScroll() { this.setState({ scroll_offset: this.ref_container.current.scrollTop diff --git a/shared/js/ui/view.tsx b/shared/js/ui/view.tsx index 15d8cc82..639116ff 100644 --- a/shared/js/ui/view.tsx +++ b/shared/js/ui/view.tsx @@ -42,6 +42,7 @@ export interface ChannelTreeEvents { notify_selection_changed: {}, notify_root_channel_changed: {}, + notify_tree_reset: {}, notify_query_view_state_changed: { queries_shown: boolean }, notify_entry_move_begin: {}, @@ -283,37 +284,6 @@ export class ChannelTree { this.events.destroy(); } - showContextMenu(x: number, y: number, on_close: () => void = undefined) { - let channelCreate = - this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_TEMPORARY).granted(1) || - this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT).granted(1) || - this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_PERMANENT).granted(1); - - contextmenu.spawn_context_menu(x, y, - { - type: contextmenu.MenuEntryType.ENTRY, - icon_class: "client-channel_create", - name: tr("Create channel"), - invalidPermission: !channelCreate, - callback: () => this.spawnCreateChannel() - }, - contextmenu.Entry.HR(), - { - type: contextmenu.MenuEntryType.ENTRY, - icon_class: "client-channel_collapse_all", - name: tr("Collapse all channels"), - callback: () => this.collapse_channels() - }, - { - type: contextmenu.MenuEntryType.ENTRY, - icon_class: "client-channel_expand_all", - name: tr("Expend all channels"), - callback: () => this.expand_channels() - }, - contextmenu.Entry.CLOSE(on_close) - ); - } - initialiseHead(serverName: string, address: ServerAddress) { this.server.reset(); this.server.remote_address = Object.assign({}, address); @@ -572,6 +542,36 @@ export class ChannelTree { return undefined; } + showContextMenu(x: number, y: number, on_close: () => void = undefined) { + let channelCreate = + this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_TEMPORARY).granted(1) || + this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT).granted(1) || + this.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_PERMANENT).granted(1); + + contextmenu.spawn_context_menu(x, y, + { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-channel_create", + name: tr("Create channel"), + invalidPermission: !channelCreate, + callback: () => this.spawnCreateChannel() + }, + contextmenu.Entry.HR(), + { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-channel_collapse_all", + name: tr("Collapse all channels"), + callback: () => this.collapse_channels() + }, + { + type: contextmenu.MenuEntryType.ENTRY, + icon_class: "client-channel_expand_all", + name: tr("Expend all channels"), + callback: () => this.expand_channels() + }, + contextmenu.Entry.CLOSE(on_close) + ); + } private open_multiselect_context_menu(entries: ChannelTreeEntry[], x: number, y: number) { const clients = entries.filter(e => e instanceof ClientEntry) as ClientEntry[]; const channels = entries.filter(e => e instanceof ChannelEntry) as ChannelEntry[]; @@ -811,13 +811,9 @@ export class ChannelTree { this.channels = []; this.channel_last = undefined; this.channel_first = undefined; + this.events.fire("notify_tree_reset"); } finally { - try { - this.events.fire_async("notify_root_channel_changed", undefined, () => flush_batched_updates(BatchUpdateType.CHANNEL_TREE)); - } catch (e) { - flush_batched_updates(BatchUpdateType.CHANNEL_TREE); - throw e; - } + flush_batched_updates(BatchUpdateType.CHANNEL_TREE); } }