diff --git a/package.json b/package.json index 509b7825c..591ae3f8c 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "react": "^16.8.6", "react-dom": "^16.8.6", "react-hot-loader": "^4.9.0", + "react-redux": "^7.1.0", "react-router-dom": "^5.0.1", "react-transition-group": "^4.1.0", "socket.io-client": "^2.2.0", @@ -42,6 +43,7 @@ "@types/query-string": "^6.3.0", "@types/react": "^16.8.19", "@types/react-dom": "^16.8.4", + "@types/react-redux": "^7.1.1", "@types/react-router-dom": "^4.3.3", "@types/react-transition-group": "^2.9.2", "@types/webpack-env": "^1.13.6", diff --git a/resources/scripts/components/App.tsx b/resources/scripts/components/App.tsx index 880bca8a0..98650be64 100644 --- a/resources/scripts/components/App.tsx +++ b/resources/scripts/components/App.tsx @@ -6,6 +6,7 @@ import { store } from '@/state'; import DashboardRouter from '@/routers/DashboardRouter'; import ServerRouter from '@/routers/ServerRouter'; import AuthenticationRouter from '@/routers/AuthenticationRouter'; +import { Provider } from 'react-redux'; interface WindowWithUser extends Window { PterodactylUser?: { @@ -37,17 +38,19 @@ const App = () => { return ( - -
- - - - - - - -
-
+ + +
+ + + + + + + +
+
+
); }; diff --git a/resources/scripts/components/server/Console.tsx b/resources/scripts/components/server/Console.tsx index 8f23c4a3a..e21d4d07a 100644 --- a/resources/scripts/components/server/Console.tsx +++ b/resources/scripts/components/server/Console.tsx @@ -1,9 +1,10 @@ -import React, { createRef, useEffect, useRef } from 'react'; +import React, { createRef } from 'react'; import { Terminal } from 'xterm'; import * as TerminalFit from 'xterm/lib/addons/fit/fit'; import SpinnerOverlay from '@/components/elements/SpinnerOverlay'; -import { State, useStoreState } from 'easy-peasy'; import { ApplicationState } from '@/state/types'; +import { connect } from 'react-redux'; +import { Websocket } from '@/plugins/Websocket'; const theme = { background: 'transparent', @@ -26,11 +27,14 @@ const theme = { brightWhite: '#ffffff', }; -export default () => { - const { instance, connected } = useStoreState((state: State) => state.server.socket); +interface Props { + connected: boolean; + instance: Websocket | null; +} - const ref = createRef(); - const terminal = useRef(new Terminal({ +class Console extends React.PureComponent> { + ref = createRef(); + terminal = new Terminal({ disableStdin: true, cursorStyle: 'underline', allowTransparency: true, @@ -38,55 +42,79 @@ export default () => { fontFamily: 'Menlo, Monaco, Consolas, monospace', rows: 30, theme: theme, - })); - - const handleServerLog = (lines: string[]) => lines.forEach(data => { - return data.split(/\n/g).forEach(line => terminal.current.writeln(line + '\u001b[0m')); }); - const handleConsoleOutput = (line: string) => terminal.current.writeln(line.replace(/(?:\r\n|\r|\n)$/im, '') + '\u001b[0m'); + componentDidMount () { + if (this.ref.current) { + this.terminal.open(this.ref.current); + this.terminal.clear(); - useEffect(() => { - ref.current && terminal.current.open(ref.current); - - // @see https://github.com/xtermjs/xterm.js/issues/2265 - // @see https://github.com/xtermjs/xterm.js/issues/2230 - TerminalFit.fit(terminal.current); - }, []); - - useEffect(() => { - if (connected && instance) { - instance.addListener('server log', handleServerLog); - instance.addListener('console output', handleConsoleOutput); - instance.send('send logs'); + // @see https://github.com/xtermjs/xterm.js/issues/2265 + // @see https://github.com/xtermjs/xterm.js/issues/2230 + TerminalFit.fit(this.terminal); } - }, [connected]); - useEffect(() => () => { - if (instance) { - instance.removeListener('server log', handleServerLog); - instance.removeListener('console output', handleConsoleOutput); + if (this.props.connected && this.props.instance) { + this.listenForEvents(); } - }, []); + } - return ( -
- -
-
-
-
-
$
-
- + componentDidUpdate (prevProps: Readonly>) { + if (!prevProps.connected && this.props.connected) { + this.listenForEvents(); + } + } + + componentWillUnmount () { + if (this.props.instance) { + this.props.instance.removeListener('server log', this.handleServerLog); + this.props.instance.removeListener('server log', this.handleConsoleOutput); + } + } + + listenForEvents () { + const instance = this.props.instance!; + + instance.addListener('server log', this.handleServerLog); + instance.addListener('console output', this.handleConsoleOutput); + instance.send('send logs'); + } + + handleServerLog = (lines: string[]) => lines.forEach(data => { + return data.split(/\n/g).forEach(line => this.terminal.writeln(line + '\u001b[0m')); + }); + + handleConsoleOutput = (line: string) => this.terminal.writeln( + line.replace(/(?:\r\n|\r|\n)$/im, '') + '\u001b[0m' + ); + + render () { + return ( +
+ +
+
+
+
+
$
+
+ +
-
- ); -}; + ); + } +} + +export default connect( + (state: ApplicationState) => ({ + connected: state.server.socket.connected, + instance: state.server.socket.instance, + }), +)(Console); diff --git a/yarn.lock b/yarn.lock index 6609f8f34..d114ed8fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -605,7 +605,7 @@ "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.4.0": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.5": version "7.4.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12" dependencies: @@ -778,6 +778,13 @@ version "4.7.2" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.2.tgz#0e670ea254d559241b6eeb3894f8754991e73220" +"@types/hoist-non-react-statics@^3.3.0": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/lodash@^4.14.119": version "4.14.119" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39" @@ -798,6 +805,15 @@ dependencies: "@types/react" "*" +"@types/react-redux@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.1.tgz#eb01e89cf71cad77df9f442b819d5db692b997cb" + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + redux "^4.0.0" + "@types/react-router-dom@^4.3.3": version "4.3.3" resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.3.tgz#7837e3e9fefbc84a8f6c8a51dca004f4e83e94e3" @@ -3966,7 +3982,7 @@ interpret@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" -invariant@^2.2.0, invariant@^2.2.2: +invariant@^2.2.0, invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" dependencies: @@ -6094,7 +6110,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@^15.5.10, prop-types@^15.6.1, prop-types@^15.6.2: +prop-types@^15.5.10, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" dependencies: @@ -6272,7 +6288,7 @@ react-hot-loader@^4.9.0: shallowequal "^1.0.2" source-map "^0.7.3" -react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6: version "16.8.6" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" @@ -6280,6 +6296,17 @@ react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" +react-redux@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.1.0.tgz#72af7cf490a74acdc516ea9c1dd80e25af9ea0b2" + dependencies: + "@babel/runtime" "^7.4.5" + hoist-non-react-statics "^3.3.0" + invariant "^2.2.4" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^16.8.6" + react-router-dom@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be" @@ -6408,7 +6435,7 @@ redux-thunk@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" -redux@^4.0.1: +redux@^4.0.0, redux@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5" dependencies: