Browse Source

Implemented a working devices list

update-deps
Alex Mikhalev 7 years ago
parent
commit
e35f9bf0d9
  1. 2
      client/App.tsx
  2. 6
      client/components/DeviceImage.tsx
  3. 72
      client/components/DeviceView.tsx
  4. 2
      client/components/NavBar.tsx
  5. 1
      client/components/index.ts
  6. 3
      client/pages/DevicePage.tsx
  7. 33
      client/pages/DevicesPage.tsx
  8. 1
      client/pages/index.tsx
  9. 3
      client/state/UserStore.ts
  10. 14
      common/httpApi/index.ts
  11. 3
      common/sprinklersRpc/websocketData.ts
  12. 2
      server/Database.ts
  13. 3
      server/entities/SprinklersDevice.ts
  14. 3
      server/entities/User.ts
  15. 2
      server/express/api/devices.ts
  16. 4
      server/express/api/users.ts
  17. 2
      server/repositories/UserRepository.ts
  18. 2
      server/sprinklersRpc/websocketServer.ts

2
client/App.tsx

@ -19,7 +19,9 @@ function NavContainer() { @@ -19,7 +19,9 @@ function NavContainer() {
<Switch>
<Route path={route.device(":deviceId")} component={p.DevicePage}/>
<Route path={route.device()} component={p.DevicesPage}/>
<Route path={route.messagesTest} component={p.MessageTest}/>
<Redirect from="/" to={route.device()} />
<Redirect to="/"/>
</Switch>

6
client/components/DeviceImage.tsx

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
import * as React from "react";
import { Item, ItemImageProps } from "semantic-ui-react";
export default function DeviceImage(props: ItemImageProps) {
return <Item.Image {...props} src={require("@client/images/raspberry_pi.png")} />;
}

72
client/components/DeviceView.tsx

@ -1,12 +1,14 @@ @@ -1,12 +1,14 @@
import * as classNames from "classnames";
import { observer } from "mobx-react";
import * as React from "react";
import { Link } from "react-router-dom";
import { Grid, Header, Icon, Item, SemanticICONS } from "semantic-ui-react";
import { DeviceImage } from "@client/components";
import * as p from "@client/pages";
import * as route from "@client/routePaths";
import { AppState, injectState } from "@client/state";
import { ConnectionState as ConState } from "@common/sprinklersRpc";
import { ConnectionState as ConState, SprinklersDevice } from "@common/sprinklersRpc";
import { Route, RouteComponentProps, withRouter } from "react-router";
import { ProgramTable, RunSectionForm, SectionRunnerView, SectionTable } from ".";
@ -44,16 +46,19 @@ const ConnectionState = observer(({ connectionState, className }: @@ -44,16 +46,19 @@ const ConnectionState = observer(({ connectionState, className }:
});
interface DeviceViewProps {
deviceId: string;
deviceId: number;
appState: AppState;
inList?: boolean;
}
class DeviceView extends React.Component<DeviceViewProps & RouteComponentProps<any>> {
render() {
const { uiStore, sprinklersRpc, routerStore } = this.props.appState;
const device = sprinklersRpc.getDevice(this.props.deviceId);
const { id, connectionState, sections, sectionRunner } = device;
const deviceBody = connectionState.isAvailable && (
renderBody(device: SprinklersDevice) {
const { inList, appState: { uiStore, routerStore } } = this.props;
const { connectionState, sectionRunner, sections } = device;
if (!connectionState.isAvailable || inList) {
return null;
}
return (
<React.Fragment>
<Grid>
<Grid.Column mobile="16" tablet="16" computer="16" largeScreen="6">
@ -70,21 +75,44 @@ class DeviceView extends React.Component<DeviceViewProps & RouteComponentProps<a @@ -70,21 +75,44 @@ class DeviceView extends React.Component<DeviceViewProps & RouteComponentProps<a
<Route path={route.program(":deviceId", ":programId")} component={p.ProgramPage} />
</React.Fragment>
);
return (
<Item>
<Item.Image src={require("@client/images/raspberry_pi.png")} />
<Item.Content className="device">
<Header as="h1">
<div>Device <kbd>{id}</kbd></div>
<ConnectionState connectionState={connectionState} />
</Header>
<Item.Meta>
Raspberry Pi Grinklers Device
</Item.Meta>
{deviceBody}
</Item.Content>
</Item>
);
}
render() {
const { deviceId, inList, appState: { sprinklersRpc, userStore } } = this.props;
const { userData } = userStore;
const iDevice = userData &&
userData.devices &&
userData.devices.find((dev) => dev.id === deviceId);
let itemContent: React.ReactNode;
if (!iDevice || !iDevice.deviceId) {
// TODO: better and link back to devices list
itemContent = <span>You do not have access to this device</span>;
} else {
const device = sprinklersRpc.getDevice(iDevice.deviceId);
const { connectionState } = device;
let header: React.ReactNode;
if (inList) { // tslint:disable-line:prefer-conditional-expression
header = <Link to={route.device(iDevice.id)}>Device <kbd>{iDevice.name}</kbd></Link>;
} else {
header = <span>Device <kbd>{iDevice.name}</kbd></span>;
}
itemContent = (
<React.Fragment>
<DeviceImage size={inList ? "tiny" : undefined} />
<Item.Content className="device">
<Header as={inList ? "h2" : "h1"}>
{header}
<ConnectionState connectionState={connectionState} />
</Header>
<Item.Meta>
Raspberry Pi Grinklers Device
</Item.Meta>
{this.renderBody(device)}
</Item.Content>
</React.Fragment>
);
}
return <Item>{itemContent}</Item>;
}
}

2
client/components/NavBar.tsx

@ -35,7 +35,7 @@ function NavBar({ appState }: { appState: AppState }) { @@ -35,7 +35,7 @@ function NavBar({ appState }: { appState: AppState }) {
}
return (
<Menu>
<NavItem to={route.device("grinklers")}>Device grinklers</NavItem>
<NavItem to={route.device()}>Devices</NavItem>
<NavItem to={route.messagesTest}>Messages test</NavItem>
<Menu.Menu position="right">
{loginMenu}

1
client/components/index.ts

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
export { default as DeviceImage } from "./DeviceImage";
export { default as DeviceView } from "./DeviceView";
export { default as DurationView } from "./DurationView";
export { default as MessagesView } from "./MessagesView";

3
client/pages/DevicePage.tsx

@ -8,9 +8,10 @@ import { RouteComponentProps, withRouter } from "react-router"; @@ -8,9 +8,10 @@ import { RouteComponentProps, withRouter } from "react-router";
class DevicePage extends React.Component<RouteComponentProps<{ deviceId: string }>> {
render() {
const { match: { params: { deviceId } } } = this.props;
const devId = Number(deviceId);
return (
<Item.Group divided>
<DeviceView deviceId={deviceId} />
<DeviceView deviceId={devId} inList={false} />
</Item.Group>
);
}

33
client/pages/DevicesPage.tsx

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
import { observer } from "mobx-react";
import * as React from "react";
import { Item } from "semantic-ui-react";
import { DeviceView } from "@client/components";
import { AppState, injectState } from "@client/state";
class DevicesPage extends React.Component<{ appState: AppState }> {
render() {
const { appState } = this.props;
const { userData } = appState.userStore;
let deviceNodes: React.ReactNode;
if (!userData) {
deviceNodes = <span>Not logged in</span>;
} else if (!userData.devices || !userData.devices.length) {
deviceNodes = <span>You have no devices</span>;
} else {
deviceNodes = userData.devices.map((device) => (
<DeviceView key={device.id} deviceId={device.id} inList />
));
}
return (
<React.Fragment>
<h1>Devices</h1>
<Item.Group>
{deviceNodes}
</Item.Group>
</React.Fragment>
);
}
}
export default injectState(observer(DevicesPage));

1
client/pages/index.tsx

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
export { default as DevicePage } from "./DevicePage";
export { default as DevicesPage } from "./DevicesPage";
export { default as LoginPage } from "./LoginPage";
export { default as LogoutPage } from "./LogoutPage";
export { default as MessageTest } from "./MessageTest";

3
client/state/UserStore.ts

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
import { IUser } from "@common/httpApi";
import { observable } from "mobx";
export class UserStore {
@observable userData: any = null;
@observable userData: IUser | null = null;
}

14
common/httpApi/index.ts

@ -15,3 +15,17 @@ export interface TokenGrantResponse { @@ -15,3 +15,17 @@ export interface TokenGrantResponse {
access_token: string;
refresh_token: string;
}
export interface IUser {
id: number;
username: string;
name: string;
devices: ISprinklersDevice[] | undefined;
}
export interface ISprinklersDevice {
id: number;
deviceId: string | null;
name: string;
users: IUser[] | undefined;
}

3
common/sprinklersRpc/websocketData.ts

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
import * as rpc from "../jsonRpc/index";
import { ErrorCode } from "@common/ErrorCode";
import { IUser } from "@common/httpApi";
import { Response as ResponseData } from "@common/sprinklersRpc/deviceRequests";
export interface IAuthenticateRequest {
@ -25,7 +26,7 @@ export interface IClientRequestTypes { @@ -25,7 +26,7 @@ export interface IClientRequestTypes {
export interface IAuthenticateResponse {
authenticated: boolean;
message: string;
user: any;
user: IUser;
}
export interface IDeviceSubscribeResponse {

2
server/Database.ts

@ -61,7 +61,7 @@ export class Database { @@ -61,7 +61,7 @@ export class Database {
}
for (let i = 0; i < NUM; i++) {
const name = "test" + i;
const name = "Test" + i;
let device = await this.sprinklersDevices.findByName(name);
if (!device) {
device = await this.sprinklersDevices.create();

3
server/entities/SprinklersDevice.ts

@ -1,9 +1,10 @@ @@ -1,9 +1,10 @@
import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from "typeorm";
import { ISprinklersDevice } from "@common/httpApi";
import { User } from "./User";
@Entity()
export class SprinklersDevice {
export class SprinklersDevice implements ISprinklersDevice {
@PrimaryGeneratedColumn()
id!: number;

3
server/entities/User.ts

@ -2,12 +2,13 @@ import * as bcrypt from "bcrypt"; @@ -2,12 +2,13 @@ import * as bcrypt from "bcrypt";
import { omit } from "lodash";
import { Column, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn } from "typeorm";
import { IUser } from "@common/httpApi";
import { SprinklersDevice} from "./SprinklersDevice";
const HASH_ROUNDS = 1;
@Entity()
export class User {
export class User implements IUser {
@PrimaryGeneratedColumn()
id!: number;

2
server/express/api/devices.ts

@ -28,7 +28,7 @@ export function devices(state: ServerState) { @@ -28,7 +28,7 @@ export function devices(state: ServerState) {
router.post("/register", verifyAuthorization({
type: "device_reg",
}), async (req, res) => {
// TODO: Implement device registration
});
return router;

4
server/express/api/users.ts

@ -23,9 +23,9 @@ export function users(state: ServerState) { @@ -23,9 +23,9 @@ export function users(state: ServerState) {
router.get("/", (req, res) => {
state.database.users.findAll()
.then((users) => {
.then((users_) => {
res.json({
data: users,
data: users_,
});
});
});

2
server/repositories/UserRepository.ts

@ -19,7 +19,7 @@ export class UserRepository extends Repository<User> { @@ -19,7 +19,7 @@ export class UserRepository extends Repository<User> {
const opts = applyDefaultOptions(options);
return super.find(opts);
}
findById(id: number, options?: Partial<FindUserOptions>) {
const opts = applyDefaultOptions(options);
return super.findOne(id, opts);

2
server/sprinklersRpc/websocketServer.ts

@ -8,10 +8,10 @@ import log from "@common/logger"; @@ -8,10 +8,10 @@ import log from "@common/logger";
import * as deviceRequests from "@common/sprinklersRpc/deviceRequests";
import * as schema from "@common/sprinklersRpc/schema";
import * as ws from "@common/sprinklersRpc/websocketData";
import { AccessToken } from "@common/TokenClaims";
import { User } from "@server/entities";
import { verifyToken } from "@server/express/authentication";
import { ServerState } from "@server/state";
import { AccessToken } from "@common/TokenClaims";
// tslint:disable:member-ordering

Loading…
Cancel
Save