/** * The main UI file for this program. */ import "./app.css"; import React from "react"; import LoggerStore from "./stores/LoggerStore"; import TopCollar from "./components/TopCollar"; import LogView from "./components/Views/Logs/LogView"; import ConfigViewer from "./components/Views/Config/ConfigViewer"; import _debounce from "lodash/debounce"; import ViewSwitcher from "./components/ViewSwitcher"; import { LauncherClient } from "../../../clients/launcherClient"; import { RouterClient } from "../../../clients/routerClient"; import FloatingFocus from "@q42/floating-focus-a11y"; // The package FloatingFocus injects a focus indicator that moves around the page, but only for // keyboard users. (Mouse users don't see the indicator.) All that needs to be done is to envoke it. new FloatingFocus(); interface IProps {} interface IState { activeView: string; clientListWidth: number; clientListHeight: number; logWidth: number; fileMode: boolean; clientListVisible: boolean; windowVisibility: string; showSearchBox: boolean; showProcessMonitor: boolean; showOverlay: boolean; overlayMessage: string | null; } const CLIENTLIST_HEIGHT_OFFSET = 0; export default class App extends React.Component { constructor(props: IProps) { super(props); const clientListVisible = LoggerStore.getClientListVisible(); this.state = { activeView: LoggerStore.getActiveView(), clientListWidth: clientListVisible ? 500 : 0, // Initialize with zeros as window is not guaranteed to be defined at initialization clientListHeight: window ? window.outerHeight - CLIENTLIST_HEIGHT_OFFSET : 0, logWidth: clientListVisible && window ? window.outerWidth - 500 : 0, fileMode: LoggerStore.getFileMode(), clientListVisible, windowVisibility: LoggerStore.getWindowVisibility(), showSearchBox: LoggerStore.getShowSearchBox(), showProcessMonitor: false, showOverlay: false, overlayMessage: null, }; this.fileMode = this.fileMode.bind(this); this.updateClientListVisible = this.updateClientListVisible.bind(this); this.updateWindowVisibility = this.updateWindowVisibility.bind(this); this.onkeydown = this.onkeydown.bind(this); this.getClientListClasses = this.getClientListClasses.bind(this); this.onResizeWindow = this.onResizeWindow.bind(this); this.setContainerWidths = _debounce(this.setContainerWidths.bind(this), CLIENTLIST_HEIGHT_OFFSET); this.setActiveView = this.setActiveView.bind(this); this.setShowSearchBox = this.setShowSearchBox.bind(this); this.getShowProcessMonitor = this.getShowProcessMonitor.bind(this); this.toggleOverlay = this.toggleOverlay.bind(this); } componentDidMount() { LoggerStore.addListener("FileMode", this.fileMode); LoggerStore.addListener("clientListVisibleChange", this.updateClientListVisible); LoggerStore.addListener("WindowVisibilityChange", this.updateWindowVisibility); LoggerStore.addListener("viewChange", this.setActiveView); LoggerStore.addListener("showSearchBoxChange", this.setShowSearchBox); LoggerStore.addListener("toggleOverlay", this.toggleOverlay); window.addEventListener("keydown", this.onkeydown); window.addEventListener("resize", this.onResizeWindow); this.getShowProcessMonitor(); } componentWillUnmount() { LoggerStore.removeListener("FileMode", this.fileMode); LoggerStore.removeListener("clientListVisibleChange", this.updateClientListVisible); LoggerStore.removeListener("WindowVisibilityChange", this.updateWindowVisibility); LoggerStore.removeListener("viewChange", this.setActiveView); LoggerStore.removeListener("showSearchBoxChange", this.setShowSearchBox); LoggerStore.removeListener("toggleOverlay", this.toggleOverlay); window.removeEventListener("resize", this.onResizeWindow); window.removeEventListener("keydown", this.onkeydown); } toggleOverlay(data: string) { this.setState({ showOverlay: !!data, overlayMessage: !!data ? data : null, }); } getShowProcessMonitor() { // Wait for the launcher to come up, then figure out whether we should show the button to spawn the process monitor. const showProcessMonitor = (err, componentList) => { this.setState({ showProcessMonitor: componentList && "Process Monitor" in componentList, }); }; RouterClient.subscribe("Launcher.update", (err, response) => { showProcessMonitor(err, response.data.componentList); }); LauncherClient.getComponentList(showProcessMonitor); } setShowSearchBox() { this.setState({ showSearchBox: LoggerStore.getShowSearchBox(), }); } onkeydown(e: KeyboardEvent) { /** * To avoid Hotkeys conflict, make sure that e.altKey * is not there because `ctrl+alt+down` minimizes all */ if (e.ctrlKey && !e.altKey) { if (e.key === "ArrowUp") { LoggerStore.setTop(); } else if (e.key === "ArrowDown") { LoggerStore.setBottom(); } else if (e.key === "ArrowLeft" || e.key === "ArrowRight") { LoggerStore.scrollToActiveRow(); } else if (e.key === "f") { LoggerStore.showSearchBox(); } return; } if (this.state.showSearchBox && e.key === "Escape") { LoggerStore.hideSearchBox(); } } setActiveView() { const activeView = LoggerStore.getActiveView(); // prevent unnecessary renders... if (activeView === this.state.activeView) return; this.setState({ activeView, }); } fileMode() { this.setState({ fileMode: LoggerStore.getFileMode(), }); } updateClientListVisible() { const clientListVisible = LoggerStore.getClientListVisible(); this.setState( { clientListVisible, logWidth: clientListVisible ? window.outerWidth - 500 : window.outerWidth - 16, clientListWidth: clientListVisible ? 500 : 0, }, LoggerStore.triggerLogRerender.bind(LoggerStore) ); } updateWindowVisibility() { this.setState({ windowVisibility: LoggerStore.getWindowVisibility(), }); } setContainerWidths(e: any) { // coming from the resize-grid. If not, coming from window. let { logWidth, clientListWidth, clientListHeight } = this.state; if (e.resizeChilds) { logWidth = e.resizeChilds[1].width; clientListWidth = e.resizeChilds[0].width; } clientListHeight = window.outerHeight - CLIENTLIST_HEIGHT_OFFSET; this.setState({ clientListWidth, logWidth, clientListHeight }); } getClientListClasses() { if (this.state.clientListWidth < 500) { return "left-panel client-list-small"; } return "left-panel"; } onResizeWindow(e: UIEvent) { LoggerStore.triggerRowheightRecalculation(); this.setContainerWidths(e); } // currently plain-text mode is never used, but hook left in case needed in future render() { if (this.state.windowVisibility === "hidden") return null; return (
{this.state.showOverlay && (
{this.state.overlayMessage}
)}
{this.state.activeView === "logger" && } {this.state.activeView === "config" && }
); } }