Alex Mikhalev
7 years ago
19 changed files with 262 additions and 130 deletions
@ -1,47 +1,37 @@ |
|||||||
import { observer } from "mobx-react"; |
|
||||||
// import DevTools from "mobx-react-devtools";
|
// import DevTools from "mobx-react-devtools";
|
||||||
import * as React from "react"; |
import * as React from "react"; |
||||||
import { Redirect, Route, RouteComponentProps, Switch } from "react-router"; |
import { Redirect, Route, Switch } from "react-router"; |
||||||
import { BrowserRouter as Router } from "react-router-dom"; |
|
||||||
import { Container } from "semantic-ui-react"; |
import { Container } from "semantic-ui-react"; |
||||||
|
|
||||||
import { DevicesView, MessagesView, MessageTest, NavBar } from "@app/components"; |
import { MessagesView, NavBar } from "@app/components"; |
||||||
|
import * as p from "@app/pages"; |
||||||
|
|
||||||
// tslint:disable:ordered-imports
|
// tslint:disable:ordered-imports
|
||||||
import "font-awesome/css/font-awesome.css"; |
import "font-awesome/css/font-awesome.css"; |
||||||
import "semantic-ui-css/semantic.css"; |
import "semantic-ui-css/semantic.css"; |
||||||
import "@app/styles/app.scss"; |
import "@app/styles/app.scss"; |
||||||
|
|
||||||
function DevicePage({match}: RouteComponentProps<{deviceId: string}>) { |
function NavContainer() { |
||||||
return ( |
return ( |
||||||
<DevicesView deviceId={match.params.deviceId}/> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
function MessagesTestPage() { |
|
||||||
return ( |
|
||||||
<MessageTest/> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
class App extends React.Component { |
|
||||||
render() { |
|
||||||
return ( |
|
||||||
<Router> |
|
||||||
<Container className="app"> |
<Container className="app"> |
||||||
<NavBar/> |
<NavBar/> |
||||||
|
|
||||||
<Switch> |
<Switch> |
||||||
<Route path="/devices/:deviceId" component={DevicePage}/> |
<Route path="/devices/:deviceId" component={p.DevicePage}/> |
||||||
<Route path="/messagesTest" component={MessagesTestPage}/> |
<Route path="/messagesTest" component={p.MessagesTestPage}/> |
||||||
<Redirect to="/"/> |
<Redirect to="/"/> |
||||||
</Switch> |
</Switch> |
||||||
|
|
||||||
<MessagesView/> |
<MessagesView/> |
||||||
</Container> |
</Container> |
||||||
</Router> |
|
||||||
); |
); |
||||||
} |
} |
||||||
} |
|
||||||
|
|
||||||
export default observer(App); |
export default function App() { |
||||||
|
return ( |
||||||
|
<Switch> |
||||||
|
<Route path="/login" component={p.LoginPage}/> |
||||||
|
<NavContainer/> |
||||||
|
</Switch> |
||||||
|
); |
||||||
|
} |
||||||
|
@ -0,0 +1,75 @@ |
|||||||
|
import { AppState, injectState } from "@app/state"; |
||||||
|
import log from "@common/logger"; |
||||||
|
import { computed, observable } from "mobx"; |
||||||
|
import { observer } from "mobx-react"; |
||||||
|
import * as React from "react"; |
||||||
|
import { Container, Dimmer, Form, Header, InputOnChangeData, Loader, Segment } from "semantic-ui-react"; |
||||||
|
|
||||||
|
class LoginPageState { |
||||||
|
@observable username = ""; |
||||||
|
@observable password = ""; |
||||||
|
|
||||||
|
@observable loading: boolean = false; |
||||||
|
|
||||||
|
@computed get canLogin() { |
||||||
|
return this.username.length > 0 && this.password.length > 0; |
||||||
|
} |
||||||
|
|
||||||
|
onUsernameChange = (e: any, data: InputOnChangeData) => { |
||||||
|
this.username = data.value; |
||||||
|
} |
||||||
|
|
||||||
|
onPasswordChange = (e: any, data: InputOnChangeData) => { |
||||||
|
this.password = data.value; |
||||||
|
} |
||||||
|
|
||||||
|
login(appState: AppState) { |
||||||
|
this.loading = true; |
||||||
|
appState.httpApi.tokenStore.grantPassword(this.username, this.password) |
||||||
|
.then(() => { |
||||||
|
this.loading = false; |
||||||
|
log.info("logged in"); |
||||||
|
appState.history.push("/"); |
||||||
|
}) |
||||||
|
.catch((err) => { |
||||||
|
this.loading = false; |
||||||
|
log.error({ err }, "login error"); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class LoginPage extends React.Component<{ appState: AppState }> { |
||||||
|
pageState = new LoginPageState(); |
||||||
|
|
||||||
|
render() { |
||||||
|
const { username, password, canLogin, loading } = this.pageState; |
||||||
|
return ( |
||||||
|
<Container className="loginPage"> |
||||||
|
<Segment> |
||||||
|
<Dimmer inverted active={loading}> |
||||||
|
<Loader/> |
||||||
|
</Dimmer> |
||||||
|
|
||||||
|
<Header as="h1">Login</Header> |
||||||
|
<Form> |
||||||
|
<Form.Input label="Username" value={username} onChange={this.pageState.onUsernameChange}/> |
||||||
|
<Form.Input |
||||||
|
label="Password" |
||||||
|
value={password} |
||||||
|
type="password" |
||||||
|
onChange={this.pageState.onPasswordChange} |
||||||
|
/> |
||||||
|
<Form.Button disabled={!canLogin} onClick={this.login}>Login</Form.Button> |
||||||
|
</Form> |
||||||
|
</Segment> |
||||||
|
</Container> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
login = () => { |
||||||
|
this.pageState.login(this.props.appState); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const DecoratedLoginPage = injectState(observer(LoginPage)); |
||||||
|
export { DecoratedLoginPage as LoginPage }; |
@ -0,0 +1,18 @@ |
|||||||
|
import * as React from "react"; |
||||||
|
import { RouteComponentProps } from "react-router"; |
||||||
|
|
||||||
|
import { DevicesView, MessageTest} from "@app/components"; |
||||||
|
|
||||||
|
export { LoginPage } from "./LoginPage"; |
||||||
|
|
||||||
|
export function DevicePage({ match }: RouteComponentProps<{ deviceId: string }>) { |
||||||
|
return ( |
||||||
|
<DevicesView deviceId={match.params.deviceId}/> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export function MessagesTestPage() { |
||||||
|
return ( |
||||||
|
<MessageTest/> |
||||||
|
); |
||||||
|
} |
@ -1,26 +1,28 @@ |
|||||||
import { WebSocketApiClient } from "@app/sprinklersRpc/websocketClient"; |
import { WebSocketRpcClient } from "@app/sprinklersRpc/websocketClient"; |
||||||
import HttpApi from "@app/state/HttpApi"; |
import HttpApi from "@app/state/HttpApi"; |
||||||
import { UiStore } from "@app/state/UiStore"; |
import { UiStore } from "@app/state/UiStore"; |
||||||
|
import { createBrowserHistory, History } from "history"; |
||||||
|
|
||||||
const isDev = process.env.NODE_ENV === "development"; |
const isDev = process.env.NODE_ENV === "development"; |
||||||
const websocketPort = isDev ? 8080 : location.port; |
const websocketPort = isDev ? 8080 : location.port; |
||||||
|
|
||||||
export default class ClientState { |
export default class AppState { |
||||||
sprinklersApi = new WebSocketApiClient(`ws://${location.hostname}:${websocketPort}`); |
history: History = createBrowserHistory(); |
||||||
uiStore = new UiStore(); |
uiStore = new UiStore(); |
||||||
httpApi = new HttpApi(); |
httpApi = new HttpApi(); |
||||||
|
tokenStore = this.httpApi.tokenStore; |
||||||
|
sprinklersRpc = new WebSocketRpcClient(`ws://${location.hostname}:${websocketPort}`, |
||||||
|
this.tokenStore); |
||||||
|
|
||||||
async start() { |
async start() { |
||||||
if (!this.httpApi.tokenStore.accessToken.isValid) { |
if (!this.httpApi.tokenStore.accessToken.isValid) { |
||||||
if (this.httpApi.tokenStore.refreshToken.isValid) { |
if (this.httpApi.tokenStore.refreshToken.isValid) { |
||||||
await this.httpApi.tokenStore.grantRefresh(); |
await this.httpApi.tokenStore.grantRefresh(); |
||||||
} else { |
} else { |
||||||
await this.httpApi.tokenStore.grantPassword("alex", "kakashka"); |
this.history.push("/login"); |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
this.sprinklersApi.accessToken = this.httpApi.tokenStore.accessToken.token!; |
this.sprinklersRpc.start(); |
||||||
|
|
||||||
this.sprinklersApi.start(); |
|
||||||
} |
} |
||||||
} |
} |
@ -1,9 +1,3 @@ |
|||||||
export { UiMessage, UiStore } from "./UiStore"; |
export { UiMessage, UiStore } from "./UiStore"; |
||||||
export * from "./reactContext"; |
export * from "./reactContext"; |
||||||
export { ClientState as StateBase } from "./ClientState"; |
export { default as AppState } from "./AppState"; |
||||||
|
|
||||||
import ClientState from "./ClientState"; |
|
||||||
|
|
||||||
|
|
||||||
export class WebApiState extends ClientState { |
|
||||||
} |
|
||||||
|
Loading…
Reference in new issue