UI for simple matrix.org bots

Part of #11

Needs actual scalar integration
This commit is contained in:
turt2live 2017-05-27 01:27:36 -06:00
parent 81ac20249e
commit 0c51d4424b
23 changed files with 212 additions and 12 deletions

View File

@ -1,3 +1,31 @@
# Configuration for the bots this Dimension supports
bots:
# Giphy (from matrix.org)
- mxid: "@neb_giphy:matrix.org"
name: "Giphy"
avatar: "/img/avatars/giphy.png"
about: "Use `!giphy query` to find an animated GIF on demand"
# Guggy (from matrix.org)
- mxid: "@neb_guggy:matrix.org"
name: "Guggy"
avatar: "/img/avatars/guggy.png"
about: "Use `!guggy sentence` to create an animated GIF from a sentence"
# Imgur (from matrix.org)
- mxid: "@_neb_imgur:matrix.org"
name: "Imgur"
avatar: "/img/avatars/imgur.png"
about: "Use `!imgur query` to find an image from Imgur"
# Wikipedia (from matrix.org)
- mxid: "@_neb_wikipedia:matrix.org"
name: "Wikipedia"
avatar: "/img/avatars/wikipedia.png"
about: "Use `!wikipedia query` to find something from Wikipedia"
# Google (from matrix.org)
- mxid: "@_neb_google:matrix.org"
name: "Google"
avatar: "/img/avatars/google.png"
about: "Use `!google image query` to find an image from Google"
# The web settings for the service (API and UI)
web:
# The port to run the webserver on

View File

@ -42,6 +42,7 @@
"@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.22",
"@types/node": "^7.0.18",
"angular2-template-loader": "^0.6.2",
"angular2-ui-switch": "^1.2.0",
"autoprefixer": "^6.7.7",
"awesome-typescript-loader": "^3.1.2",
"codelyzer": "^3.0.1",

View File

@ -41,6 +41,8 @@ class Dimension {
});
this._app.post("/api/v1/scalar/register", this._scalarRegister.bind(this));
this._app.get("/api/v1/dimension/bots", this._getBots.bind(this));
this._app.get("/api/v1/scalar/checkToken", this._checkScalarToken.bind(this));
}
start() {
@ -48,6 +50,19 @@ class Dimension {
log.info("Dimension", "API and UI listening on " + config.get("web.address") + ":" + config.get("web.port"));
}
_getBots(req, res) {
res.setHeader("Content-Type", "application/json");
res.send(JSON.stringify(config.bots));
}
_checkScalarToken(req, res) {
var token = req.query.scalar_token;
if (!token) res.sendStatus(404);
else this._db.checkToken(token).then(() => {
res.sendStatus(200);
}, () => res.sendStatus(404));
}
_scalarRegister(req, res) {
var tokenInfo = req.body;
if (!tokenInfo || !tokenInfo['access_token'] || !tokenInfo['token_type'] || !tokenInfo['matrix_server_name'] || !tokenInfo['expires_in']) {

View File

@ -5,6 +5,5 @@
main {
font-family: Arial, Helvetica, sans-serif;
text-align: center;
display: block;
}

View File

@ -7,6 +7,10 @@ import { HomeComponent } from "./home/home.component";
import { routing } from "./app.routing";
import { removeNgStyles, createNewHosts } from "@angularclass/hmr";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { RiotComponent } from "./riot/riot.component";
import { ApiService } from "./shared/api.service";
import { BotComponent } from "./bot/bot.component";
import { UiSwitchModule } from "angular2-ui-switch";
@NgModule({
imports: [
@ -14,13 +18,21 @@ import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
HttpModule,
FormsModule,
routing,
NgbModule.forRoot()
NgbModule.forRoot(),
UiSwitchModule,
],
declarations: [
AppComponent,
HomeComponent
HomeComponent,
RiotComponent,
BotComponent,
// Vendor
],
providers: [
ApiService,
// Vendor
],
bootstrap: [AppComponent],
entryComponents: []

View File

@ -1,8 +1,10 @@
import { RouterModule, Routes } from "@angular/router";
import { HomeComponent } from "./home/home.component";
import { RiotComponent } from "./riot/riot.component";
const routes: Routes = [
{path: '', component: HomeComponent},
{path: 'riot', component: RiotComponent},
];
export const routing = RouterModule.forRoot(routes);

View File

@ -0,0 +1,15 @@
<div class="bot">
<img [src]="bot.avatar" class="avatar">
<div class="title">
<b>{{ bot.name }}</b>
<div style="display: flex;">
<div class="switch">
<ui-switch [checked]="bot.isEnabled" size="small" [disabled]="updating" (change)="update()"></ui-switch>
</div>
<div class="toolbar">
<i class="fa fa-question-circle text-info" ngbTooltip="{{bot.about}}" *ngIf="bot.about"></i>
</div>
</div>
</div>
<div style="clear: both;"></div>
</div>

View File

@ -0,0 +1,35 @@
// component styles are encapsulated and only applied to their components
.bot {
flex: auto;
margin: 5px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
max-width: 200px;
min-width: 150px;
background-color: white;
}
.avatar {
float: left;
margin-right: 7px;
width: 50px;
height: 50px;
border-radius: 50px;
}
.title {
float: right;
width: calc(100% - 57px);
}
.switch {
display: inline-block;
text-align: left;
}
.toolbar {
display: inline-block;
vertical-align: top;
margin-left: 5px;
}

View File

@ -0,0 +1,23 @@
import { Component, Input } from "@angular/core";
import { Bot } from "../shared/models/bot";
@Component({
selector: 'my-bot',
templateUrl: './bot.component.html',
styleUrls: ['./bot.component.scss'],
})
export class BotComponent {
@Input() bot: Bot;
public updating = false;
public htmlAbout: string;
constructor() {
}
public update(): void {
this.updating = true;
setTimeout(() => this.updating = false, Math.random() * 15000);
}
}

View File

@ -1,4 +1 @@
// component styles are encapsulated and only applied to their components
* {
color: #FFEF00;
}

View File

@ -0,0 +1,10 @@
<div *ngIf="error">
<p class="text-danger">{{ error }}</p>
</div>
<div *ngIf="!error">
<h3>Manage Integrations</h3>
<p>Turn on anything you like. If an integration has some special configuration, it will have a configuration icon next to it.</p>
<div class="integration-container">
<my-bot *ngFor="let bot of bots" [bot]="bot"></my-bot>
</div>
</div>

View File

@ -0,0 +1,4 @@
// component styles are encapsulated and only applied to their components
.integration-container {
display: flex;
}

View File

@ -0,0 +1,32 @@
import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { ApiService } from "../shared/api.service";
import { Bot } from "../shared/models/bot";
@Component({
selector: 'my-riot',
templateUrl: './riot.component.html',
styleUrls: ['./riot.component.scss'],
})
export class RiotComponent {
public error: string;
public bots: Bot[] = [];
constructor(private activatedRoute: ActivatedRoute, private api: ApiService) {
let params: any = this.activatedRoute.snapshot.queryParams;
if (!params.scalar_token || !params.room_id) this.error = "Missing scalar token or room ID";
else this.api.checkScalarToken(params.scalar_token).then(isValid => {
if (isValid) this.init();
else this.error = "Invalid scalar token";
});
}
private init() {
this.api.getBots().then(bots => {
this.bots = bots;
bots.map(b => b.isEnabled = Math.random() > 0.75);
});
}
}

View File

@ -0,0 +1,19 @@
import { Injectable } from "@angular/core";
import { Http } from "@angular/http";
import { Bot } from "./models/bot";
@Injectable()
export class ApiService {
constructor(private http: Http) {
}
checkScalarToken(token): Promise<boolean> {
return this.http.get("/api/v1/scalar/checkToken", {params: {scalar_token: token}})
.map(res => res.status === 200).toPromise();
}
getBots(): Promise<Bot[]> {
return this.http.get("/api/v1/dimension/bots")
.map(res => res.json()).toPromise();
}
}

View File

@ -1,3 +1,5 @@
// Services
export * from './api.service';
// Models
export * from './models/bot';

View File

@ -0,0 +1,7 @@
export class Bot {
mxid: string;
name: string;
avatar: string;
about: string; // nullable
isEnabled: boolean;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

View File

@ -8,6 +8,9 @@
<link rel="icon" type="image/x-icon" href="/img/favicon.ico">
<link rel="icon" type="image/png" href="/img/favicon.png">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
<base href="/">
</head>
<body>

View File

@ -2,10 +2,6 @@
body {
background: #ddd !important;
margin: 0;
padding: 0;
color: #fff;
}
a {
color: #03A9F4;
padding: 30px;
color: #222;
}