Actions now use resolver. Dialog handles websocket messaging. Added support for text values on actions.

This commit is contained in:
Tom 2025-01-15 00:58:55 +00:00
parent 9cda9a5738
commit c62b9aaaad
8 changed files with 121 additions and 53 deletions

View File

@ -76,6 +76,21 @@
}
</mat-form-field>
}
@else if (field.type == 'text-values') {
<mat-form-field>
<mat-label>{{field.label}}</mat-label>
<mat-select [formControl]="field.control">
@for (value of field.values; track $index) {
<mat-option [value]="value">{{value}}</mat-option>
}
</mat-select>
@if (field.control.invalid && (field.control.dirty || field.control.touched)) {
@if (field.control.hasError('required')) {
<small class="error">This field is required.</small>
}
}
</mat-form-field>
}
</div>
}
@ -87,8 +102,13 @@
@if (!isNew) {
<button mat-raised-button class="delete" (click)="deleteAction(action)">Delete</button>
}
<button mat-raised-button (click)="dialogRef.close()">Cancel</button>
<button mat-raised-button disabled="{{!formsDirty || !formsValidity}}" (click)="save()">Save</button>
<button
mat-raised-button
(click)="dialogRef.close()">Cancel</button>
<button
mat-raised-button
disabled="{{!formsDirty || !formsValidity || waitForResponse}}"
(click)="save()">Save</button>
</mat-card-actions>
</mat-card>
</body>

View File

@ -25,9 +25,9 @@ import { HermesClientService } from '../../hermes-client.service';
styleUrl: './action-item-edit.component.scss'
})
export class ActionItemEditComponent implements OnInit {
readonly client = inject(HermesClientService);
private readonly client = inject(HermesClientService);
readonly dialogRef = inject(MatDialogRef<ActionItemEditComponent>);
readonly data = inject<{ action: RedeemableAction, actions:RedeemableAction[] }>(MAT_DIALOG_DATA);
private readonly data = inject<{ action: RedeemableAction, actions: RedeemableAction[] }>(MAT_DIALOG_DATA);
action = this.data.action;
actions = this.data.actions;
readonly actionEntries: ({ [key: string]: any[] }) = {
@ -203,6 +203,7 @@ export class ActionItemEditComponent implements OnInit {
isNew: boolean = true;
previousName: string = this.action.name;
waitForResponse: boolean = false;
readonly formGroup = new FormGroup({
name: new FormControl(this.action.name, [Validators.required]),
@ -237,8 +238,13 @@ export class ActionItemEditComponent implements OnInit {
if (this.isNew)
return;
this.client.first((d: any) => d.op == 4 && d.d.request.type == 'delete_redeemable_action' && d.d.request.data.name == this.action.name)
.subscribe({
next: () => this.dialogRef.close(),
error: () => this.waitForResponse = false,
complete: () => this.waitForResponse = false,
});
this.client.deleteRedeemableAction(action.name);
this.dialogRef.close();
}
save(): void {
@ -262,6 +268,17 @@ export class ActionItemEditComponent implements OnInit {
return;
}
this.dialogRef.close(this.action);
const isNewAction = !this.action.user_id;
const requestType = isNewAction ? 'create_redeemable_action' : 'update_redeemable_action';
this.client.first((d: any) => d.op == 4 && d.d.request.type == requestType && d.d.data.name == this.action.name)
.subscribe({
next: () => this.dialogRef.close(this.action),
error: () => this.waitForResponse = false,
complete: () => this.waitForResponse = false,
});
if (isNewAction)
this.client.createRedeemableAction(this.action.name, this.action.type, this.action.data);
else
this.client.updateRedeemableAction(this.action.name, this.action.type, this.action.data);
}
}

View File

@ -2,18 +2,6 @@ main {
display: grid;
grid-template-columns: repeat(1, 1fr);
@media (min-width:1200px) {
grid-template-columns: repeat(2, 1fr);
}
@media (min-width:1650px) {
grid-template-columns: repeat(3, 1fr);
}
@media (min-width:2200px) {
grid-template-columns: repeat(4, 1fr);
}
grid-auto-flow: row dense;
grid-gap: 1rem;
justify-content: center;
@ -54,3 +42,21 @@ main {
flex: 1;
}
}
@media (min-width:1200px) {
main {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width:1650px) {
main {
grid-template-columns: repeat(3, 1fr);
}
}
@media (min-width:2200px) {
main {
grid-template-columns: repeat(4, 1fr);
}
}

View File

@ -37,30 +37,21 @@ export class ActionListComponent {
this.opened = true;
const dialogRef = this.dialog.open(ActionItemEditComponent, {
data: { action: {user_id: action.user_id, name: action.name, type: action.type, data: action.data }, actions: this.actions },
data: { action: { user_id: action.user_id, name: action.name, type: action.type, data: action.data }, actions: this.actions },
});
const isNewAction = action.name.length <= 0;
const requestType = isNewAction ? 'create_redeemable_action' : 'update_redeemable_action';
dialogRef.afterClosed().subscribe((result: RedeemableAction) => {
const isNewAction = action.name.length <= 0;
dialogRef.afterClosed().subscribe((result: RedeemableAction | undefined) => {
this.opened = false;
if (!result)
return;
this.client.first((d: any) => d.op == 4 && d.d.request.type == requestType && d.d.data.name == result.name)
?.subscribe(_ => {
if (isNewAction) {
this.actionsChange.emit(result);
} else {
action.type = result.type;
action.data = result.data;
}
});
if (isNewAction)
this.client.createRedeemableAction(result.name, result.type, result.data);
else
this.client.updateRedeemableAction(result.name, result.type, result.data);
if (isNewAction) {
this.actionsChange.emit(result);
} else {
action.type = result.type;
action.data = result.data;
}
});
}
}

View File

@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ActionsComponent } from './actions/actions.component';
import { ActionListComponent } from './action-list/action-list.component';
import { ActionItemComponent } from './action-item/action-item.component';
import { ActionItemEditComponent } from './action-item-edit/action-item-edit.component';
@ -11,7 +11,7 @@ import { ActionItemComponent } from './action-item/action-item.component';
imports: [
ActionsComponent,
ActionListComponent,
ActionItemComponent,
ActionItemEditComponent,
]
})
export class ActionsModule { }

View File

@ -4,7 +4,7 @@
<section>
<article>
<mat-form-field>
<mat-label>Filter</mat-label>
<mat-label>Filter by type</mat-label>
<mat-select (selectionChange)="onFilterChange($event.value)" value="0">
<mat-select-trigger>
<mat-icon matPrefix>filter_list</mat-icon>&nbsp;{{filter.name}}

View File

@ -7,6 +7,8 @@ import { MatIconModule } from '@angular/material/icon';
import { HermesClientService } from '../../hermes-client.service';
import RedeemableAction from '../../shared/models/redeemable_action';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { RedeemableActionService } from '../../shared/services/redeemable-action.service';
interface IActionFilter {
name: string
@ -38,27 +40,30 @@ export class ActionsComponent implements OnInit {
{ name: 'Veadotube', filter: data => data.type.includes('VEADOTUBE') },
];
client = inject(HermesClientService);
private readonly client = inject(HermesClientService);
private readonly redeemableActionService = inject(RedeemableActionService);
private readonly route = inject(ActivatedRoute);
filter = this.filters[0];
searchControl = new FormControl('');
search = '';
items: RedeemableAction[] = [];
ngOnInit(): void {
this.client.subscribeToRequests('get_redeemable_actions', d => {
this.items = d.data;
});
this.client.subscribeToRequests('create_redeemable_action', d => {
if (d.request.nounce != null && d.request.nounce.startsWith(this.client.session_id)) {
this.route.data.subscribe(data => {
if (!data['redeemableActions'])
return;
this.actions = [...data['redeemableActions']];
});
this.redeemableActionService.create$?.subscribe(d => {
if (d.error || d.request.nounce != null && d.request.nounce.startsWith(this.client.session_id))
return;
}
this.actions.push(d.data);
});
this.client.subscribeToRequests('update_redeemable_action', d => {
if (d.request.nounce != null && d.request.nounce.startsWith(this.client.session_id)) {
this.redeemableActionService.update$?.subscribe(d => {
if (d.error || d.request.nounce != null && d.request.nounce.startsWith(this.client.session_id))
return;
}
const action = this.actions.find(a => a.name == d.data.name);
if (action) {
@ -66,7 +71,10 @@ export class ActionsComponent implements OnInit {
action.data = d.data.data;
}
});
this.client.subscribeToRequests('delete_redeemable_action', d => {
this.redeemableActionService.delete$?.subscribe(d => {
if (d.error)
return;
this.items = this.actions.filter(a => a.name != d.request.data.name);
});

View File

@ -1,17 +1,43 @@
import { inject, Injectable } from '@angular/core';
import { HermesClientService } from '../../hermes-client.service';
import { map, of } from 'rxjs';
import { map, Observable, of } from 'rxjs';
import RedeemableAction from '../models/redeemable_action';
import EventService from './EventService';
@Injectable({
providedIn: 'root'
})
export class RedeemableActionService {
private client = inject(HermesClientService);
private readonly client = inject(HermesClientService);
private readonly events = inject(EventService);
private data: RedeemableAction[] = []
loaded = false;
private loaded = false;
create$: Observable<any> | undefined;
update$: Observable<any> | undefined;
delete$: Observable<any> | undefined;
constructor() {
this.create$ = this.client.filterByRequestType('create_redeemable_action');
this.update$ = this.client.filterByRequestType('update_redeemable_action');
this.delete$ = this.client.filterByRequestType('delete_redeemable_action');
this.create$?.subscribe(d => this.data.push(d.data));
this.update$?.subscribe(d => {
const action = this.data.find(r => r.name == d.data.name);
if (action) {
action.type = d.data.type;
action.data = d.data.data;
}
});
this.delete$?.subscribe(d => 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);