mirror of
https://github.com/turt2live/matrix-dimension.git
synced 2024-10-01 01:05:53 -04:00
UI for simple matrix.org bots
Part of #11 Needs actual scalar integration
This commit is contained in:
parent
81ac20249e
commit
0c51d4424b
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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']) {
|
||||
|
@ -5,6 +5,5 @@
|
||||
|
||||
main {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
|
@ -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: []
|
||||
|
@ -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);
|
||||
|
15
web/app/bot/bot.component.html
Normal file
15
web/app/bot/bot.component.html
Normal 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>
|
35
web/app/bot/bot.component.scss
Normal file
35
web/app/bot/bot.component.scss
Normal 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;
|
||||
}
|
23
web/app/bot/bot.component.ts
Normal file
23
web/app/bot/bot.component.ts
Normal 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);
|
||||
}
|
||||
}
|
@ -1,4 +1 @@
|
||||
// component styles are encapsulated and only applied to their components
|
||||
* {
|
||||
color: #FFEF00;
|
||||
}
|
10
web/app/riot/riot.component.html
Normal file
10
web/app/riot/riot.component.html
Normal 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>
|
4
web/app/riot/riot.component.scss
Normal file
4
web/app/riot/riot.component.scss
Normal file
@ -0,0 +1,4 @@
|
||||
// component styles are encapsulated and only applied to their components
|
||||
.integration-container {
|
||||
display: flex;
|
||||
}
|
32
web/app/riot/riot.component.ts
Normal file
32
web/app/riot/riot.component.ts
Normal 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);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
19
web/app/shared/api.service.ts
Normal file
19
web/app/shared/api.service.ts
Normal 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();
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// Services
|
||||
export * from './api.service';
|
||||
|
||||
// Models
|
||||
export * from './models/bot';
|
||||
|
7
web/app/shared/models/bot.ts
Normal file
7
web/app/shared/models/bot.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export class Bot {
|
||||
mxid: string;
|
||||
name: string;
|
||||
avatar: string;
|
||||
about: string; // nullable
|
||||
isEnabled: boolean;
|
||||
}
|
BIN
web/public/img/avatars/giphy.png
Normal file
BIN
web/public/img/avatars/giphy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
BIN
web/public/img/avatars/google.png
Normal file
BIN
web/public/img/avatars/google.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
web/public/img/avatars/guggy.png
Normal file
BIN
web/public/img/avatars/guggy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 74 KiB |
BIN
web/public/img/avatars/imgur.png
Normal file
BIN
web/public/img/avatars/imgur.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
web/public/img/avatars/wikipedia.png
Normal file
BIN
web/public/img/avatars/wikipedia.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 129 KiB |
@ -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>
|
||||
|
@ -2,10 +2,6 @@
|
||||
body {
|
||||
background: #ddd !important;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #03A9F4;
|
||||
padding: 30px;
|
||||
color: #222;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user