Added connections. Added url redirect for login.
This commit is contained in:
parent
56deb3384c
commit
6e5efab5ec
@ -33,10 +33,21 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
this.subscriptions = [];
|
this.subscriptions = [];
|
||||||
|
|
||||||
this.subscriptions.push(this.events.listen('tts_login_ack', async _ => {
|
this.subscriptions.push(this.events.listen('tts_login_ack', async _ => {
|
||||||
await this.router.navigate(['policies'])
|
const url = router.url;
|
||||||
|
const params = router.parseUrl(url).queryParams;
|
||||||
|
|
||||||
|
if (params && 'rd' in params) {
|
||||||
|
await this.router.navigate([params['rd']]);
|
||||||
|
} else if (url == '/' || url.startsWith('/login') || url.startsWith('/tts-login')) {
|
||||||
|
await this.router.navigate(['policies']);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
this.subscriptions.push(this.events.listen('tts_logoff', async _ => {
|
this.subscriptions.push(this.events.listen('tts_logoff', async _ => {
|
||||||
await this.router.navigate(['tts-login'])
|
await this.router.navigate(['tts-login'], {
|
||||||
|
queryParams: {
|
||||||
|
rd: this.router.url.substring(1)
|
||||||
|
}
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,32 +20,40 @@ import { GroupsComponent } from './groups/groups/groups.component';
|
|||||||
import { GroupPageComponent } from './groups/group-page/group-page.component';
|
import { GroupPageComponent } from './groups/group-page/group-page.component';
|
||||||
import GroupChatterResolver from './shared/resolvers/group-chatter-resolver';
|
import GroupChatterResolver from './shared/resolvers/group-chatter-resolver';
|
||||||
import PermissionResolver from './shared/resolvers/permission-resolver';
|
import PermissionResolver from './shared/resolvers/permission-resolver';
|
||||||
|
import { ConnectionsComponent } from './connections/connections/connections.component';
|
||||||
|
import ConnectionResolver from './shared/resolvers/connection-resolver';
|
||||||
|
import { ConnectionCallbackComponent } from './connections/callback/callback.component';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: 'policies',
|
path: 'actions',
|
||||||
component: PolicyComponent,
|
component: ActionsComponent,
|
||||||
canActivate: [AuthUserGuard],
|
canActivate: [AuthUserGuard],
|
||||||
resolve: {
|
resolve: {
|
||||||
groups: GroupResolver,
|
redeemableActions: RedeemableActionResolver,
|
||||||
policies: PolicyResolver,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'auth',
|
||||||
|
component: TwitchAuthCallbackComponent,
|
||||||
|
canActivate: [AuthVisitorGuard],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'connections',
|
||||||
|
component: ConnectionsComponent,
|
||||||
|
canActivate: [AuthUserGuard],
|
||||||
|
resolve: {
|
||||||
|
connections: ConnectionResolver,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'connections/callback',
|
||||||
|
component: ConnectionCallbackComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'groups',
|
path: 'groups',
|
||||||
component: GroupsComponent,
|
component: GroupsComponent,
|
||||||
canActivate: [AuthAdminGuard],
|
canActivate: [AuthUserGuard],
|
||||||
resolve: {
|
|
||||||
groups: GroupResolver,
|
|
||||||
chatters: GroupChatterResolver,
|
|
||||||
policies: PolicyResolver,
|
|
||||||
permissions: PermissionResolver,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'groups/:id',
|
|
||||||
component: GroupPageComponent,
|
|
||||||
canActivate: [AuthAdminGuard],
|
|
||||||
resolve: {
|
resolve: {
|
||||||
groups: GroupResolver,
|
groups: GroupResolver,
|
||||||
chatters: GroupChatterResolver,
|
chatters: GroupChatterResolver,
|
||||||
@ -62,11 +70,28 @@ export const routes: Routes = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'actions',
|
path: 'groups/:id',
|
||||||
component: ActionsComponent,
|
component: GroupPageComponent,
|
||||||
canActivate: [AuthUserGuard],
|
canActivate: [AuthUserGuard],
|
||||||
resolve: {
|
resolve: {
|
||||||
redeemableActions: RedeemableActionResolver,
|
groups: GroupResolver,
|
||||||
|
chatters: GroupChatterResolver,
|
||||||
|
policies: PolicyResolver,
|
||||||
|
permissions: PermissionResolver,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'login',
|
||||||
|
component: LoginComponent,
|
||||||
|
canActivate: [AuthVisitorGuard],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'policies',
|
||||||
|
component: PolicyComponent,
|
||||||
|
canActivate: [AuthUserGuard],
|
||||||
|
resolve: {
|
||||||
|
groups: GroupResolver,
|
||||||
|
policies: PolicyResolver,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -79,11 +104,6 @@ export const routes: Routes = [
|
|||||||
twitchRedemptions: TwitchRedemptionResolver,
|
twitchRedemptions: TwitchRedemptionResolver,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'login',
|
|
||||||
component: LoginComponent,
|
|
||||||
canActivate: [AuthVisitorGuard],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'tts-login',
|
path: 'tts-login',
|
||||||
component: TtsLoginComponent,
|
component: TtsLoginComponent,
|
||||||
@ -92,9 +112,4 @@ export const routes: Routes = [
|
|||||||
keys: ApiKeyResolver,
|
keys: ApiKeyResolver,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'auth',
|
|
||||||
component: TwitchAuthCallbackComponent,
|
|
||||||
canActivate: [AuthVisitorGuard],
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
3
src/app/connections/callback/callback.component.html
Normal file
3
src/app/connections/callback/callback.component.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@if (success || failure) {
|
||||||
|
<p>Automatically going back to the connections page soon...</p>
|
||||||
|
}
|
23
src/app/connections/callback/callback.component.spec.ts
Normal file
23
src/app/connections/callback/callback.component.spec.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CallbackComponent } from './callback.component';
|
||||||
|
|
||||||
|
describe('CallbackComponent', () => {
|
||||||
|
let component: CallbackComponent;
|
||||||
|
let fixture: ComponentFixture<CallbackComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [CallbackComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(CallbackComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
48
src/app/connections/callback/callback.component.ts
Normal file
48
src/app/connections/callback/callback.component.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { Component, inject, OnInit } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { HermesClientService } from '../../hermes-client.service';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'connection-callback',
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './callback.component.html',
|
||||||
|
styleUrl: './callback.component.scss'
|
||||||
|
})
|
||||||
|
export class ConnectionCallbackComponent implements OnInit {
|
||||||
|
private readonly client = inject(HermesClientService);
|
||||||
|
private readonly http = inject(HttpClient);
|
||||||
|
private readonly router = inject(Router);
|
||||||
|
|
||||||
|
success: boolean = false;
|
||||||
|
failure: boolean = false;
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
const url = this.router.parseUrl(this.router.url);
|
||||||
|
if (!url.fragment) {
|
||||||
|
this.failure = true;
|
||||||
|
await this.router.navigate(['connections']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const paramsParts = url.fragment.split('&');
|
||||||
|
const params = Object.assign({}, ...paramsParts.map((p: string) => ({ [p.split('=')[0]]: p.split('=')[1] })));
|
||||||
|
|
||||||
|
if (!params.access_token || !params.scope || !params.state || !params.token_type) {
|
||||||
|
this.failure = true;
|
||||||
|
await this.router.navigate(['connections']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.http.get(`https://beta.tomtospeech.com/api/auth/connections?token=${params['access_token']}&state=${params['state']}&expires_in=${params['expires_in']}`).subscribe(async (d: any) => {
|
||||||
|
const data = d.data;
|
||||||
|
this.success = true;
|
||||||
|
|
||||||
|
await setTimeout(async () => {
|
||||||
|
this.client.createConnection(data.connection.name, data.connection.type, data.connection.clientId, params['access_token'], data.connection.grantType, params['scope'], data.expires_at);
|
||||||
|
await this.router.navigate(['connections']);
|
||||||
|
}, 2000)
|
||||||
|
});
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
<mat-card>
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title-group>
|
||||||
|
<mat-card-title>Add Connection</mat-card-title>
|
||||||
|
<mat-card-subtitle></mat-card-subtitle>
|
||||||
|
</mat-card-title-group>
|
||||||
|
</mat-card-header>
|
||||||
|
|
||||||
|
<mat-card-content>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>Connection Name</mat-label>
|
||||||
|
<input matInput
|
||||||
|
[formControl]="nameControl" />
|
||||||
|
@if (nameControl.invalid && (nameControl.dirty || nameControl.touched)) {
|
||||||
|
@if (nameControl.hasError('required')) {
|
||||||
|
<small class="error">This field is required.</small>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>Client Type</mat-label>
|
||||||
|
<mat-select [formControl]="typeControl">
|
||||||
|
<mat-option value="nightbot">Nightbot</mat-option>
|
||||||
|
<mat-option value="twitch">Twitch</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
@if (typeControl.invalid && (typeControl.dirty || typeControl.touched)) {
|
||||||
|
@if (typeControl.hasError('required')) {
|
||||||
|
<small class="error">This field is required.</small>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<mat-label>Client Id</mat-label>
|
||||||
|
<input matInput
|
||||||
|
[formControl]="clientIdControl" />
|
||||||
|
@if (clientIdControl.invalid && (clientIdControl.dirty || clientIdControl.touched)) {
|
||||||
|
@if (clientIdControl.hasError('required')) {
|
||||||
|
<small class="error">This field is required.</small>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</mat-form-field>
|
||||||
|
</mat-card-content>
|
||||||
|
|
||||||
|
<mat-card-actions class="actions"
|
||||||
|
align="end">
|
||||||
|
<button mat-raised-button
|
||||||
|
class="warning"
|
||||||
|
(click)="dialogRef.close()">Cancel</button>
|
||||||
|
<button mat-raised-button
|
||||||
|
class="confirm"
|
||||||
|
disabled="{{form.invalid || waitForResponse}}"
|
||||||
|
(click)="submit()">Add</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
|
||||||
|
@if (responseError) {
|
||||||
|
<mat-card-footer>
|
||||||
|
<small class="error below">{{responseError}}</small>
|
||||||
|
</mat-card-footer>
|
||||||
|
}
|
||||||
|
</mat-card>
|
@ -0,0 +1,12 @@
|
|||||||
|
.mat-mdc-form-field {
|
||||||
|
display: block;
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-card-actions {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-card-actions > button {
|
||||||
|
margin: 1em;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ConnectionItemEditComponent } from './connection-item-edit.component';
|
||||||
|
|
||||||
|
describe('ConnectionItemEditComponent', () => {
|
||||||
|
let component: ConnectionItemEditComponent;
|
||||||
|
let fixture: ComponentFixture<ConnectionItemEditComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ConnectionItemEditComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ConnectionItemEditComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,72 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Component, Inject, inject } from '@angular/core';
|
||||||
|
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { ActionItemEditComponent } from '../../actions/action-item-edit/action-item-edit.component';
|
||||||
|
import { HermesClientService } from '../../hermes-client.service';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'connection-item-edit',
|
||||||
|
imports: [
|
||||||
|
MatButtonModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatSelectModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
],
|
||||||
|
templateUrl: './connection-item-edit.component.html',
|
||||||
|
styleUrl: './connection-item-edit.component.scss'
|
||||||
|
})
|
||||||
|
export class ConnectionItemEditComponent {
|
||||||
|
private readonly client = inject(HermesClientService);
|
||||||
|
private readonly http = inject(HttpClient);
|
||||||
|
|
||||||
|
readonly data = inject<{ name: string }>(MAT_DIALOG_DATA);
|
||||||
|
readonly nameControl = new FormControl<string>('', [Validators.required]);
|
||||||
|
readonly clientIdControl = new FormControl<string>('', [Validators.required]);
|
||||||
|
readonly typeControl = new FormControl<string>('', [Validators.required]);
|
||||||
|
readonly form = new FormGroup({
|
||||||
|
name: this.nameControl,
|
||||||
|
clientId: this.clientIdControl,
|
||||||
|
type: this.typeControl,
|
||||||
|
});
|
||||||
|
|
||||||
|
readonly dialogRef = inject(MatDialogRef<ActionItemEditComponent>);
|
||||||
|
|
||||||
|
responseError: string | undefined;
|
||||||
|
waitForResponse = false;
|
||||||
|
|
||||||
|
constructor(@Inject(DOCUMENT) private document: Document) { }
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.nameControl.setValue(this.data.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
submit(): void {
|
||||||
|
if (this.form.invalid || this.waitForResponse) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.http.post('/api/auth/connections', {
|
||||||
|
name: this.nameControl.value,
|
||||||
|
type: this.typeControl.value,
|
||||||
|
client_id: this.clientIdControl.value,
|
||||||
|
grant_type: 'bearer',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
|
||||||
|
}
|
||||||
|
}).subscribe(async (d: any) => this.document.location.href = d.data);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
<section [class.twitch]="connection().type == 'twitch'"
|
||||||
|
[class.spotify]="connection().type == 'spotify'">
|
||||||
|
{{connection().name}}
|
||||||
|
|
||||||
|
<article class="right">
|
||||||
|
<button mat-button
|
||||||
|
class="neutral"
|
||||||
|
(click)="renew(connection())">
|
||||||
|
<mat-icon>refresh</mat-icon>
|
||||||
|
Renew
|
||||||
|
</button>
|
||||||
|
<button mat-button
|
||||||
|
class="danger"
|
||||||
|
(click)="delete(connection())">
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</article>
|
||||||
|
</section>
|
@ -0,0 +1,11 @@
|
|||||||
|
section {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin: 0 0.5em;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ConnectionItemComponent } from './connection-item.component';
|
||||||
|
|
||||||
|
describe('ConnectionItemComponent', () => {
|
||||||
|
let component: ConnectionItemComponent;
|
||||||
|
let fixture: ComponentFixture<ConnectionItemComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ConnectionItemComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ConnectionItemComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,49 @@
|
|||||||
|
import { Component, Inject, inject, input } from '@angular/core';
|
||||||
|
import { Connection } from '../../shared/models/connection';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { HermesClientService } from '../../hermes-client.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'connection-item',
|
||||||
|
imports: [
|
||||||
|
MatButtonModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
],
|
||||||
|
templateUrl: './connection-item.component.html',
|
||||||
|
styleUrl: './connection-item.component.scss'
|
||||||
|
})
|
||||||
|
export class ConnectionItemComponent {
|
||||||
|
router = inject(Router);
|
||||||
|
http = inject(HttpClient);
|
||||||
|
client = inject(HermesClientService);
|
||||||
|
|
||||||
|
connection = input.required<Connection>();
|
||||||
|
|
||||||
|
constructor(@Inject(DOCUMENT) private document: Document) { }
|
||||||
|
|
||||||
|
delete(conn: Connection) {
|
||||||
|
this.client.deleteConnection(conn.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
renew(conn: Connection) {
|
||||||
|
this.http.post('/api/auth/connections', {
|
||||||
|
name: conn.name,
|
||||||
|
type: conn.type,
|
||||||
|
client_id: conn.client_id,
|
||||||
|
grant_type: conn.grant_type,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + localStorage.getItem('jwt')
|
||||||
|
}
|
||||||
|
}).subscribe(async (d: any) => this.document.location.href = d.data);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
<ul>
|
||||||
|
<li class="header">
|
||||||
|
<mat-form-field appearance="outline"
|
||||||
|
subscriptSizing="dynamic">
|
||||||
|
<mat-label>Name Filter</mat-label>
|
||||||
|
<input matInput
|
||||||
|
placeholder="Filter connections by name"
|
||||||
|
[formControl]="searchControl" />
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline"
|
||||||
|
subscriptSizing="dynamic">
|
||||||
|
<mat-label>Type Filter</mat-label>
|
||||||
|
<mat-select [formControl]="typeControl">
|
||||||
|
<mat-option value="">All</mat-option>
|
||||||
|
<mat-option value="nightbot">Nightbot</mat-option>
|
||||||
|
<mat-option value="twitch">Twitch</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<button mat-icon-button
|
||||||
|
(click)="add()">
|
||||||
|
<mat-icon>add</mat-icon>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
@for (connection of connections; track $index) {
|
||||||
|
<li>
|
||||||
|
<connection-item [connection]="connection" />
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
@if (!connections.length) {
|
||||||
|
@if (searchControl.value) {
|
||||||
|
<p class="notice">No connections matches the filter.</p>
|
||||||
|
} @else {
|
||||||
|
<p class="notice">No connections available.</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</ul>
|
@ -0,0 +1,38 @@
|
|||||||
|
@use '@angular/material' as mat;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
@include mat.all-component-densities(-5);
|
||||||
|
|
||||||
|
@include mat.form-field-overrides((
|
||||||
|
outlined-outline-color: rgb(167, 88, 199),
|
||||||
|
outlined-focus-label-text-color: rgb(155, 57, 194),
|
||||||
|
outlined-focus-outline-color: rgb(155, 57, 194),
|
||||||
|
));
|
||||||
|
|
||||||
|
background-color: rgb(202, 68, 255);
|
||||||
|
border-radius: 15px;
|
||||||
|
margin: 0 0;
|
||||||
|
padding: 0;
|
||||||
|
max-width: 500px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
background-color: rgb(240, 165, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li.header {
|
||||||
|
background-color: rgb(215, 115, 255);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul .notice {
|
||||||
|
text-align: center;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ConnectionListComponent } from './connection-list.component';
|
||||||
|
|
||||||
|
describe('ConnectionListComponent', () => {
|
||||||
|
let component: ConnectionListComponent;
|
||||||
|
let fixture: ComponentFixture<ConnectionListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ConnectionListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ConnectionListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,62 @@
|
|||||||
|
import { Component, inject, Input } from '@angular/core';
|
||||||
|
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { Connection } from '../../shared/models/connection';
|
||||||
|
import { ConnectionItemComponent } from "../connection-item/connection-item.component";
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { ConnectionItemEditComponent } from '../connection-item-edit/connection-item-edit.component';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
|
import { containsLettersInOrder } from '../../shared/utils/string-compare';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'connection-list',
|
||||||
|
imports: [
|
||||||
|
MatButtonModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatSelectModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
ConnectionItemComponent,
|
||||||
|
],
|
||||||
|
templateUrl: './connection-list.component.html',
|
||||||
|
styleUrl: './connection-list.component.scss'
|
||||||
|
})
|
||||||
|
export class ConnectionListComponent {
|
||||||
|
private readonly _dialog = inject(MatDialog);
|
||||||
|
|
||||||
|
private _connections: Connection[] = [];
|
||||||
|
|
||||||
|
readonly searchControl = new FormControl<string>('');
|
||||||
|
readonly typeControl = new FormControl<string>('');
|
||||||
|
|
||||||
|
opened = false;
|
||||||
|
|
||||||
|
|
||||||
|
get connections() {
|
||||||
|
return this._connections.filter(c => containsLettersInOrder(c.name, this.searchControl.value) && (!this.typeControl.value || c.type == this.typeControl.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input({ required: true })
|
||||||
|
set connections(value: Connection[]) {
|
||||||
|
this._connections = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
add() {
|
||||||
|
if (this.opened)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.opened = true;
|
||||||
|
|
||||||
|
const dialogRef = this._dialog.open(ConnectionItemEditComponent, {
|
||||||
|
data: { name: this.searchControl.value },
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe((_: any) => this.opened = false);
|
||||||
|
}
|
||||||
|
}
|
12
src/app/connections/connections.module.ts
Normal file
12
src/app/connections/connections.module.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [],
|
||||||
|
imports: [
|
||||||
|
CommonModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class ConnectionsModule { }
|
@ -0,0 +1,3 @@
|
|||||||
|
<h3>Connections</h3>
|
||||||
|
|
||||||
|
<connection-list [connections]="connections"/>
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ConnectionsComponent } from './connections.component';
|
||||||
|
|
||||||
|
describe('ConnectionsComponent', () => {
|
||||||
|
let component: ConnectionsComponent;
|
||||||
|
let fixture: ComponentFixture<ConnectionsComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ConnectionsComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ConnectionsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
41
src/app/connections/connections/connections.component.ts
Normal file
41
src/app/connections/connections/connections.component.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { Component, inject, OnDestroy } from '@angular/core';
|
||||||
|
import { Connection } from '../../shared/models/connection';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { ConnectionListComponent } from "../connection-list/connection-list.component";
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { ConnectionService } from '../../shared/services/connection.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'connections',
|
||||||
|
imports: [ConnectionListComponent],
|
||||||
|
templateUrl: './connections.component.html',
|
||||||
|
styleUrl: './connections.component.scss'
|
||||||
|
})
|
||||||
|
export class ConnectionsComponent implements OnDestroy {
|
||||||
|
private readonly route = inject(ActivatedRoute);
|
||||||
|
private readonly connectionService = inject(ConnectionService);
|
||||||
|
subscriptions: (Subscription | undefined)[] = [];
|
||||||
|
connections: Connection[] = [];
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.route.data.subscribe(payload => {
|
||||||
|
this.connections = payload['connections'] ?? [];
|
||||||
|
});
|
||||||
|
|
||||||
|
this.subscriptions.push(this.connectionService.delete$?.subscribe(d => {
|
||||||
|
if (d.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.connectionService.fetch().subscribe(connections => this.connections = connections);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
for (let subscription of this.subscriptions) {
|
||||||
|
if (subscription)
|
||||||
|
subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,13 +4,17 @@ article {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
padding: 1em;
|
padding: 0.5em 1em;
|
||||||
|
|
||||||
& :first-child {
|
& > :first-child {
|
||||||
min-width: 180px;
|
min-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
& :not(:first-child) {
|
& > :last-child {
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > :not(:first-child) {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,28 @@ export class HermesClientService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public createConnection(name: string, type: string, client_id: string, access_token: string, grant_type: string, scope: string, expiration: Date) {
|
||||||
|
if (!this.logged_in)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.send(3, {
|
||||||
|
request_id: null,
|
||||||
|
type: "create_connection",
|
||||||
|
data: { name, type, client_id, access_token, grant_type, scope, expiration },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public createConnectionState(name: string, type: string, client_id: string, grant_type: string) {
|
||||||
|
if (!this.logged_in)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.send(3, {
|
||||||
|
request_id: null,
|
||||||
|
type: "create_connection_state",
|
||||||
|
data: { name, type, client_id, grant_type },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public createGroup(name: string, priority: number) {
|
public createGroup(name: string, priority: number) {
|
||||||
if (!this.logged_in)
|
if (!this.logged_in)
|
||||||
return;
|
return;
|
||||||
@ -170,6 +192,28 @@ export class HermesClientService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public deleteConnection(name: string) {
|
||||||
|
if (!this.logged_in)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.send(3, {
|
||||||
|
request_id: null,
|
||||||
|
type: "delete_connection",
|
||||||
|
data: { name },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteConnectionState(name: string) {
|
||||||
|
if (!this.logged_in)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.send(3, {
|
||||||
|
request_id: null,
|
||||||
|
type: "delete_connection_state",
|
||||||
|
data: { name },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public deleteGroup(id: string) {
|
public deleteGroup(id: string) {
|
||||||
if (!this.logged_in)
|
if (!this.logged_in)
|
||||||
return;
|
return;
|
||||||
@ -250,6 +294,28 @@ export class HermesClientService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fetchConnections() {
|
||||||
|
if (!this.logged_in)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.send(3, {
|
||||||
|
request_id: null,
|
||||||
|
type: "get_connections",
|
||||||
|
data: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public fetchConnectionStates() {
|
||||||
|
if (!this.logged_in)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.send(3, {
|
||||||
|
request_id: null,
|
||||||
|
type: "get_connection_states",
|
||||||
|
data: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public fetchFilters() {
|
public fetchFilters() {
|
||||||
if (!this.logged_in)
|
if (!this.logged_in)
|
||||||
return;
|
return;
|
||||||
|
@ -42,14 +42,19 @@
|
|||||||
Redemptions
|
Redemptions
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@if (isAdmin()) {
|
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/groups"
|
<a routerLink="/groups"
|
||||||
routerLinkActive="active">
|
routerLinkActive="active">
|
||||||
Groups
|
Groups
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a routerLink="/connections"
|
||||||
|
routerLinkActive="active">
|
||||||
|
Connections
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
@ -69,7 +69,6 @@ export class PermissionItemEditComponent implements OnInit {
|
|||||||
this.client.first((d: any) => d.op == 4 && d.d.request.type == 'create_group_permission' && d.d.request.data.path == this.pathControl.value)
|
this.client.first((d: any) => d.op == 4 && d.d.request.type == 'create_group_permission' && d.d.request.data.path == this.pathControl.value)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: (d) => {
|
next: (d) => {
|
||||||
console.log('sdifhsdiofs data', d);
|
|
||||||
if (d.d.error) {
|
if (d.d.error) {
|
||||||
this.responseError = d.d.error;
|
this.responseError = d.d.error;
|
||||||
} else {
|
} else {
|
||||||
|
7
src/app/shared/models/connection-state.ts
Normal file
7
src/app/shared/models/connection-state.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export interface ConnectionState {
|
||||||
|
user_id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
client_id: string;
|
||||||
|
grant_type: string;
|
||||||
|
}
|
11
src/app/shared/models/connection.ts
Normal file
11
src/app/shared/models/connection.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export interface Connection {
|
||||||
|
user_id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
client_id: string;
|
||||||
|
access_token: string;
|
||||||
|
grant_type: string;
|
||||||
|
scope: string;
|
||||||
|
expires_at: Date;
|
||||||
|
default: boolean;
|
||||||
|
}
|
14
src/app/shared/resolvers/connection-resolver.ts
Normal file
14
src/app/shared/resolvers/connection-resolver.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { Connection } from '../models/connection';
|
||||||
|
import { ConnectionService } from '../services/connection.service';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export default class ConnectionResolver implements Resolve<Connection[]> {
|
||||||
|
constructor(private service: ConnectionService) { }
|
||||||
|
|
||||||
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Connection[]> {
|
||||||
|
return this.service.fetch();
|
||||||
|
}
|
||||||
|
}
|
14
src/app/shared/resolvers/connection-state-resolver.ts
Normal file
14
src/app/shared/resolvers/connection-state-resolver.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { ConnectionService } from '../services/connection.service';
|
||||||
|
import { ConnectionState } from '../models/connection-state';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export default class ConnectionResolver implements Resolve<ConnectionState[]> {
|
||||||
|
constructor(private service: ConnectionService) { }
|
||||||
|
|
||||||
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<ConnectionState[]> {
|
||||||
|
return this.service.fetch();
|
||||||
|
}
|
||||||
|
}
|
16
src/app/shared/services/connection-state.service.spec.ts
Normal file
16
src/app/shared/services/connection-state.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ConnectionStateService } from './connection-state.service';
|
||||||
|
|
||||||
|
describe('ConnectionStateService', () => {
|
||||||
|
let service: ConnectionStateService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(ConnectionStateService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
74
src/app/shared/services/connection-state.service.ts
Normal file
74
src/app/shared/services/connection-state.service.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { inject, Injectable } from '@angular/core';
|
||||||
|
import { HermesClientService } from '../../hermes-client.service';
|
||||||
|
import EventService from './EventService';
|
||||||
|
import { map, Observable, of } from 'rxjs';
|
||||||
|
import { Connection } from '../models/connection';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ConnectionStateService {
|
||||||
|
private readonly client = inject(HermesClientService);
|
||||||
|
private readonly events = inject(EventService);
|
||||||
|
private data: Connection[] = [];
|
||||||
|
private loaded = false;
|
||||||
|
create$: Observable<any> | undefined;
|
||||||
|
update$: Observable<any> | undefined;
|
||||||
|
delete$: Observable<any> | undefined;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.create$ = this.client.filterByRequestType('create_connection_state');
|
||||||
|
this.delete$ = this.client.filterByRequestType('delete_connection_state');
|
||||||
|
|
||||||
|
this.create$?.subscribe(d => {
|
||||||
|
if (d.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data.push(d.data);
|
||||||
|
});
|
||||||
|
this.update$?.subscribe(d => {
|
||||||
|
if (d.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const connection = this.data.find(p => p.name == d.data.name);
|
||||||
|
if (connection) {
|
||||||
|
connection.type = d.data.type;
|
||||||
|
connection.client_id = d.data.client_id;
|
||||||
|
connection.access_token = d.data.access_token;
|
||||||
|
connection.grant_type = d.data.grant_type;
|
||||||
|
connection.scope = d.data.scope;
|
||||||
|
connection.expires_at = d.data.expires_at;
|
||||||
|
connection.default = d.data.default;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.delete$?.subscribe(d => {
|
||||||
|
if (d.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data = this.data.filter(r => r.name != d.request.data.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.events.listen('tts_logoff', () => {
|
||||||
|
this.data = [];
|
||||||
|
this.loaded = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fetch() {
|
||||||
|
if (this.loaded) {
|
||||||
|
return of(this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const $ = this.client.first(d => d.d.request.type == 'get_connection_states')!.pipe(map(d => d.d.data));
|
||||||
|
$.subscribe(d => {
|
||||||
|
this.data = d;
|
||||||
|
this.loaded = true;
|
||||||
|
});
|
||||||
|
this.client.fetchConnectionStates();
|
||||||
|
return $;
|
||||||
|
}
|
||||||
|
}
|
16
src/app/shared/services/connection.service.spec.ts
Normal file
16
src/app/shared/services/connection.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ConnectionService } from './connection.service';
|
||||||
|
|
||||||
|
describe('ConnectionService', () => {
|
||||||
|
let service: ConnectionService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(ConnectionService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
75
src/app/shared/services/connection.service.ts
Normal file
75
src/app/shared/services/connection.service.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { inject, Injectable } from '@angular/core';
|
||||||
|
import { HermesClientService } from '../../hermes-client.service';
|
||||||
|
import EventService from './EventService';
|
||||||
|
import { map, Observable, of } from 'rxjs';
|
||||||
|
import { Connection } from '../models/connection';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ConnectionService {
|
||||||
|
private readonly client = inject(HermesClientService);
|
||||||
|
private readonly events = inject(EventService);
|
||||||
|
private data: Connection[] = [];
|
||||||
|
private loaded = false;
|
||||||
|
create$: Observable<any> | undefined;
|
||||||
|
update$: Observable<any> | undefined;
|
||||||
|
delete$: Observable<any> | undefined;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.create$ = this.client.filterByRequestType('create_connection');
|
||||||
|
this.update$ = this.client.filterByRequestType('update_connection');
|
||||||
|
this.delete$ = this.client.filterByRequestType('delete_connection');
|
||||||
|
|
||||||
|
this.create$?.subscribe(d => {
|
||||||
|
if (d.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data.push(d.data);
|
||||||
|
});
|
||||||
|
this.update$?.subscribe(d => {
|
||||||
|
if (d.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const connection = this.data.find(p => p.name == d.data.name);
|
||||||
|
if (connection) {
|
||||||
|
connection.type = d.data.type;
|
||||||
|
connection.client_id = d.data.client_id;
|
||||||
|
connection.access_token = d.data.access_token;
|
||||||
|
connection.grant_type = d.data.grant_type;
|
||||||
|
connection.scope = d.data.scope;
|
||||||
|
connection.expires_at = d.data.expires_at;
|
||||||
|
connection.default = d.data.default;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.delete$?.subscribe(d => {
|
||||||
|
if (d.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data = this.data.filter(r => r.name != d.request.data.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.events.listen('tts_logoff', () => {
|
||||||
|
this.data = [];
|
||||||
|
this.loaded = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fetch() {
|
||||||
|
if (this.loaded) {
|
||||||
|
return of(this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const $ = this.client.first(d => d.d.request.type == 'get_connections')!.pipe(map(d => d.d.data));
|
||||||
|
$.subscribe(d => {
|
||||||
|
this.data = d;
|
||||||
|
this.loaded = true;
|
||||||
|
});
|
||||||
|
this.client.fetchConnections();
|
||||||
|
return $;
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,6 @@ import { Group } from '../../shared/models/group';
|
|||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { group } from 'console';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-twitch-user-item-add',
|
selector: 'app-twitch-user-item-add',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user