diff --git a/src/app/actions/action-dropdown/action-dropdown.component.ts b/src/app/actions/action-dropdown/action-dropdown.component.ts index a771a29..c07c9c1 100644 --- a/src/app/actions/action-dropdown/action-dropdown.component.ts +++ b/src/app/actions/action-dropdown/action-dropdown.component.ts @@ -1,9 +1,8 @@ -import { Component, EventEmitter, inject, input, Input, OnInit, Output } from '@angular/core'; -import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { ActivatedRoute } from '@angular/router'; import RedeemableAction from '../../shared/models/redeemable-action'; @Component({ @@ -21,20 +20,9 @@ export class ActionDropdownComponent implements OnInit { @Input() action: string | undefined; @Output() readonly actionChange = new EventEmitter(); - private readonly route = inject(ActivatedRoute); - errorMessageKeys: string[] = [] - constructor() { - this.route.data.subscribe(data => { - if (!data['redeemableActions']) - return; - - this.actions = data['redeemableActions']; - }); - } - ngOnInit(): void { this.errorMessageKeys = Object.keys(this.errorMessages); diff --git a/src/app/actions/actions/actions.component.ts b/src/app/actions/actions/actions.component.ts index 06eea2f..3d0b761 100644 --- a/src/app/actions/actions/actions.component.ts +++ b/src/app/actions/actions/actions.component.ts @@ -58,7 +58,7 @@ export class ActionsComponent implements OnInit, OnDestroy { ngOnInit(): void { this.route.data.subscribe(data => this._actions = data['redeemableActions'] || []); - this.subscriptions.push(this.redeemableActionService.delete$?.subscribe(_ => this.redeemableActionService.fetch().subscribe(a => this._actions = a))); + this.subscriptions.push(this.redeemableActionService.changes$?.subscribe(a => this._actions = a)); this.client.fetchRedeemableActions(); } diff --git a/src/app/redemptions/redemption-item-edit/redemption-item-edit.component.ts b/src/app/redemptions/redemption-item-edit/redemption-item-edit.component.ts index 06a9a40..fafe63f 100644 --- a/src/app/redemptions/redemption-item-edit/redemption-item-edit.component.ts +++ b/src/app/redemptions/redemption-item-edit/redemption-item-edit.component.ts @@ -1,4 +1,4 @@ -import { Component, inject, Input, model, OnInit, signal } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; import Redemption from '../../shared/models/redemption'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; @@ -14,8 +14,6 @@ import TwitchRedemption from '../../shared/models/twitch-redemption'; import RedeemableAction from '../../shared/models/redeemable-action'; import { integerValidator } from '../../shared/validators/integer'; import { createTypeValidator } from '../../shared/validators/of-type'; -import RedemptionService from '../../shared/services/redemption.service'; -import { Subscription } from 'rxjs'; @Component({ selector: 'redemption-item-edit', @@ -33,10 +31,9 @@ import { Subscription } from 'rxjs'; styleUrl: './redemption-item-edit.component.scss' }) export class RedemptionItemEditComponent implements OnInit { - readonly client = inject(HermesClientService); - readonly redemptionService = inject(RedemptionService); - readonly dialogRef = inject(MatDialogRef); - readonly data = inject<{ redemption: Redemption, twitchRedemptions: TwitchRedemption[], redeemableActions: RedeemableAction[] }>(MAT_DIALOG_DATA); + private readonly client = inject(HermesClientService); + private readonly dialogRef = inject(MatDialogRef); + private readonly data = inject<{ redemption: Redemption, twitchRedemptions: { [id: string]: TwitchRedemption }, redeemableActions: RedeemableAction[] }>(MAT_DIALOG_DATA); redemptionFormControl = new FormControl(undefined, [Validators.required, createTypeValidator('Object')]); redemptionErrorMessages: { [errorKey: string]: string } = { @@ -66,16 +63,17 @@ export class RedemptionItemEditComponent implements OnInit { }); redemption: Redemption = { id: '', user_id: '', redemption_id: '', action_name: '', order: 0, state: true }; - twitchRedemptions: TwitchRedemption[] = []; + twitchRedemptions: { [id: string]: TwitchRedemption } = {}; redeemableActions: RedeemableAction[] = []; + waitForResponse = false; responseError: string | undefined = undefined; ngOnInit(): void { this.redemption = this.data.redemption; - this.orderFormControl.setValue(this.redemption.order); this.twitchRedemptions = this.data.twitchRedemptions; this.redeemableActions = this.data.redeemableActions; + this.orderFormControl.setValue(this.redemption.order); this.orderErrorMessageKeys = Object.keys(this.orderErrorMessages); } @@ -84,7 +82,9 @@ export class RedemptionItemEditComponent implements OnInit { if (this.waitForResponse) return; - this.waitForResponse = true + this.waitForResponse = true; + this.responseError = undefined; + const id = this.redemption.id; this.client.first((d: any) => d.op == 4 && d.d.request.type == 'delete_redemption' && d.d.request.data.id == id) ?.subscribe({ @@ -107,6 +107,7 @@ export class RedemptionItemEditComponent implements OnInit { this.waitForResponse = true; this.responseError = undefined; + const order = this.orderFormControl.value; if (order == null) { this.responseError = 'Order must be an integer.'; @@ -114,16 +115,14 @@ export class RedemptionItemEditComponent implements OnInit { return; } - this.waitForResponse = true; const isNew = !this.redemption.id; if (isNew) { - this.client.first((d: any) => d.op == 4 && d.d.request.type == 'create_redemption' && d.d.request.data.action == (this.redemption.action_name ?? '') && d.d.request.data.redemption == (this.redemption.redemption_id ?? '')) + this.client.first((d: any) => d.op == 4 && d.d.request.type == 'create_redemption' && d.d.request.data.action == this.redemption.action_name && d.d.request.data.redemption == this.redemption.redemption_id) ?.subscribe({ next: (d) => { if (d.d.error) { this.responseError = d.d.error; } else { - this.redemption.order = order; this.dialogRef.close(d.d.data); } }, @@ -132,13 +131,13 @@ export class RedemptionItemEditComponent implements OnInit { }); this.client.createRedemption(this.redemption.redemption_id, this.redemption.action_name, order); } else { + this.client.first((d: any) => d.op == 4 && d.d.request.type == 'update_redemption' && d.d.data.id == this.redemption.id) ?.subscribe({ next: (d) => { if (d.d.error) { this.responseError = d.d.error; } else { - this.redemption.order = order; this.dialogRef.close(d.d.data); } }, diff --git a/src/app/redemptions/redemption-list/redemption-list.component.html b/src/app/redemptions/redemption-list/redemption-list.component.html index 6f4ba36..9284020 100644 --- a/src/app/redemptions/redemption-list/redemption-list.component.html +++ b/src/app/redemptions/redemption-list/redemption-list.component.html @@ -14,10 +14,12 @@
- - +
diff --git a/src/app/redemptions/redemption-list/redemption-list.component.ts b/src/app/redemptions/redemption-list/redemption-list.component.ts index 6da1052..b47ccff 100644 --- a/src/app/redemptions/redemption-list/redemption-list.component.ts +++ b/src/app/redemptions/redemption-list/redemption-list.component.ts @@ -1,23 +1,19 @@ -import { Component, inject, OnDestroy, signal } from '@angular/core'; -import RedemptionService from '../../shared/services/redemption.service'; +import { Component, inject, input, signal } from '@angular/core'; import Redemption from '../../shared/models/redemption'; import { MatCardModule } from '@angular/material/card'; import { TwitchRedemptionDropdownComponent } from "../twitch-redemption-dropdown/twitch-redemption-dropdown.component"; import { MatFormFieldModule } from '@angular/material/form-field'; -import { ActivatedRoute } from '@angular/router'; import { ActionDropdownComponent } from '../../actions/action-dropdown/action-dropdown.component'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatTableModule } from '@angular/material/table'; -import TwitchRedemption from '../../shared/models/twitch-redemption'; import { RedemptionItemEditComponent } from '../redemption-item-edit/redemption-item-edit.component'; import { MatDialog } from '@angular/material/dialog'; import RedeemableAction from '../../shared/models/redeemable-action'; import { MatExpansionModule } from '@angular/material/expansion'; import { ScrollingModule } from '@angular/cdk/scrolling'; -import { Subscription } from 'rxjs'; -import { HermesClientService } from '../../hermes-client.service'; +import { toTwitchRedemptionDict } from '../../shared/transformers/twitch-redemption.transformer'; @Component({ selector: 'redemption-list', @@ -36,97 +32,45 @@ import { HermesClientService } from '../../hermes-client.service'; templateUrl: './redemption-list.component.html', styleUrl: './redemption-list.component.scss' }) -export class RedemptionListComponent implements OnDestroy { - private readonly client = inject(HermesClientService); - private readonly redemptionService = inject(RedemptionService); - private readonly route = inject(ActivatedRoute); +export class RedemptionListComponent { readonly dialog = inject(MatDialog); - private _redemptions: Redemption[] = []; - private _twitchRedemptions: TwitchRedemption[] = []; - private _twitchRedemptionsDict: { [id: string]: string } = {}; - private _actions: RedeemableAction[] = []; displayedColumns: string[] = ['twitch-redemption', 'action-name', 'order', 'misc']; filter_redemption: string | undefined; filter_action_name: string | undefined; readonly panelOpenState = signal(false); - private _subscriptions: Subscription[] = [] + _redemptions = input.required({ alias: 'redemptions' }); + twitchRedemptions = input.required({ + alias: 'twitchRedemptions', + transform: toTwitchRedemptionDict, + }); + //twitchRedemptions = computed<{ [id: string]: string } | null>(() => ({})); + actions = input.required(); - constructor() { - this.route.data.subscribe(r => { - this._twitchRedemptions = r['twitchRedemptions']; - this._twitchRedemptionsDict = Object.assign({}, ...r['twitchRedemptions'].map((t: TwitchRedemption) => ({ [t.id]: t.title }))); - this._actions = r['redeemableActions']; - - let redemptions = [...r['redemptions']]; - redemptions.sort((a: Redemption, b: Redemption) => this.compare(a, b)); - this._redemptions = redemptions; - }); - - let subscription = this.redemptionService.create$?.subscribe(d => { - if (d.error || !d.data || d.request.nounce != null && d.request.nounce.startsWith(this.client.session_id)) - return; - - let index = -1; - for (let i = 0; i < this._redemptions.length; i++) { - const comp = this.compare(d.data, this._redemptions[i]); - if (comp < 0) { - index = i; - break; - } - } - this._redemptions.splice(index >= 0 ? index : this._redemptions.length, 0, d.data); - }); - if (subscription) - this._subscriptions.push(subscription); - - subscription = this.redemptionService.update$?.subscribe(d => { - if (d.error || !d.data || d.request.nounce != null && d.request.nounce.startsWith(this.client.session_id)) - return; - - const redemption = this._redemptions.find(r => r.id = d.data.id); - if (redemption) { - redemption.action_name = d.data.action_name; - redemption.redemption_id = d.data.redemption_id; - redemption.order = d.data.order; - redemption.state = d.data.state; - } - }); - if (subscription) - this._subscriptions.push(subscription); - - subscription = this.redemptionService.delete$?.subscribe(d => { - if (d.error || d.request.nounce != null && d.request.nounce.startsWith(this.client.session_id)) - return; - - this._redemptions = this._redemptions.filter(r => r.id != d.request.data.id); - }); - if (subscription) - this._subscriptions.push(subscription); - } - - ngOnInit() { - this.panelOpenState.set(false); - } - - ngOnDestroy(): void { - this._subscriptions.forEach(s => s.unsubscribe()); - } compare(a: Redemption, b: Redemption) { - return this._twitchRedemptionsDict[a.redemption_id].localeCompare(this._twitchRedemptionsDict[b.redemption_id]) || a.order - b.order; + const dict = this.twitchRedemptions(); + if (!dict) { + return 0; + } + return dict[a.redemption_id]?.title.localeCompare(dict[b.redemption_id]?.title) || a.order - b.order; } get redemptions() { - const redemptionFilter = this.filter_redemption?.toString().toLowerCase(); const actionFilter = this.filter_action_name?.toString().toLowerCase(); - let filtered = this._redemptions.filter(r => !redemptionFilter || this._twitchRedemptionsDict[r.redemption_id].toLowerCase().includes(redemptionFilter)); - filtered = filtered.filter(r => !actionFilter || r.action_name.toLowerCase().includes(actionFilter)); + const redemptionFilter = this.filter_redemption?.toString().toLowerCase(); + let filtered = this._redemptions(); + if (redemptionFilter) { + filtered = filtered.filter(r => this.twitchRedemptions()![r.redemption_id]?.title.toLowerCase().includes(redemptionFilter) || r.redemption_id == redemptionFilter); + } + if (actionFilter) { + filtered = filtered.filter(r => !actionFilter || r.action_name.toLowerCase().includes(actionFilter)); + } return filtered; } getTwitchRedemptionNameById(id: string) { - return this._twitchRedemptionsDict[id]; + return this.twitchRedemptions()![id]?.title; } add(): void { @@ -134,39 +78,9 @@ export class RedemptionListComponent implements OnDestroy { } openDialog(redemption: Redemption): void { - const dialogRef = this.dialog.open(RedemptionItemEditComponent, { - data: { redemption: { ...redemption }, twitchRedemptions: this._twitchRedemptions, redeemableActions: this._actions }, + this.dialog.open(RedemptionItemEditComponent, { + data: { redemption: { ...redemption }, twitchRedemptions: this.twitchRedemptions(), redeemableActions: this.actions() }, maxWidth: '100vw' }); - - dialogRef.afterClosed().subscribe((result: Redemption | string) => { - if (!result) - return; - - if (typeof result === 'string') { - // Deleted - this._redemptions = this._redemptions.filter(r => r.id != result); - } else { - const redemption = this._redemptions.find(r => r.id == result.id); - if (redemption) { - // Updated - redemption.action_name = result.action_name; - redemption.redemption_id = result.redemption_id; - redemption.order = result.order; - redemption.state = result.state; - } else { - // Created - let index = -1; - for (let i = 0; i < this._redemptions.length; i++) { - const comp = this.compare(result, this._redemptions[i]); - if (comp < 0) { - index = i; - break; - } - } - this._redemptions.splice(index >= 0 ? index : this._redemptions.length, 0, result); - } - } - }); } } \ No newline at end of file diff --git a/src/app/redemptions/redemptions/redemptions.component.html b/src/app/redemptions/redemptions/redemptions.component.html index bcfb356..a74abe1 100644 --- a/src/app/redemptions/redemptions/redemptions.component.html +++ b/src/app/redemptions/redemptions/redemptions.component.html @@ -1,5 +1,7 @@
- +
\ No newline at end of file diff --git a/src/app/redemptions/redemptions/redemptions.component.ts b/src/app/redemptions/redemptions/redemptions.component.ts index f94a817..a350d5c 100644 --- a/src/app/redemptions/redemptions/redemptions.component.ts +++ b/src/app/redemptions/redemptions/redemptions.component.ts @@ -1,26 +1,25 @@ -import { Component, inject, OnInit } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { RedemptionListComponent } from "../redemption-list/redemption-list.component"; -import { HermesClientService } from '../../hermes-client.service'; -import { HttpClient } from '@angular/common/http'; import RedemptionService from '../../shared/services/redemption.service'; -import { Observable, of } from 'rxjs'; -import Redemption from '../../shared/models/redemption'; -import { ActivatedRoute } from '@angular/router'; +import { AsyncPipe } from '@angular/common'; +import TwitchRedemptionService from '../../shared/services/twitch-redemption.service'; +import RedeemableActionService from '../../shared/services/redeemable-action.service'; @Component({ selector: 'redemptions', - imports: [RedemptionListComponent], + imports: [ + AsyncPipe, + RedemptionListComponent + ], templateUrl: './redemptions.component.html', styleUrl: './redemptions.component.scss' }) -export class RedemptionsComponent implements OnInit { - client = inject(HermesClientService); - http = inject(HttpClient); - route = inject(ActivatedRoute); - redemptionService = inject(RedemptionService); - redemptions: Observable | undefined; +export class RedemptionsComponent { + private readonly twitchRedemptionService = inject(TwitchRedemptionService); + private readonly redemptionService = inject(RedemptionService); + private readonly actionService = inject(RedeemableActionService); - ngOnInit(): void { - - } + redemptions$ = this.redemptionService.changes$; + twitchRedemptions$ = this.twitchRedemptionService.fetch(); + actions$ = this.actionService.changes$; } diff --git a/src/app/redemptions/twitch-redemption-dropdown/twitch-redemption-dropdown.component.html b/src/app/redemptions/twitch-redemption-dropdown/twitch-redemption-dropdown.component.html index c21645d..c47367a 100644 --- a/src/app/redemptions/twitch-redemption-dropdown/twitch-redemption-dropdown.component.html +++ b/src/app/redemptions/twitch-redemption-dropdown/twitch-redemption-dropdown.component.html @@ -11,7 +11,7 @@ - @for (redemption of filteredRedemptions; track redemption.id) { + @for (redemption of filteredRedemptions; track redemption.title) { {{redemption.title}} } diff --git a/src/app/redemptions/twitch-redemption-dropdown/twitch-redemption-dropdown.component.ts b/src/app/redemptions/twitch-redemption-dropdown/twitch-redemption-dropdown.component.ts index c2f4a56..9ba9c14 100644 --- a/src/app/redemptions/twitch-redemption-dropdown/twitch-redemption-dropdown.component.ts +++ b/src/app/redemptions/twitch-redemption-dropdown/twitch-redemption-dropdown.component.ts @@ -1,10 +1,10 @@ -import { Component, EventEmitter, inject, input, Input, OnInit, Output } from '@angular/core'; -import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; +import { Component, input, Input, model, OnInit } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { MatFormFieldModule } from '@angular/material/form-field'; import TwitchRedemption from '../../shared/models/twitch-redemption'; import { MatInputModule } from '@angular/material/input'; -import { ActivatedRoute } from '@angular/router'; +import { toTwitchRedemptionArray } from '../../shared/transformers/twitch-redemption.transformer'; @Component({ selector: 'twitch-redemption-dropdown', @@ -16,69 +16,60 @@ export class TwitchRedemptionDropdownComponent implements OnInit { @Input() formControl = new FormControl(undefined); @Input() errorMessages: { [errorKey: string]: string } = {}; - @Input() search: boolean = false; - @Input() twitchRedemptions: TwitchRedemption[] = []; - @Input() twitchRedemptionId: string | undefined; - @Output() readonly twitchRedemptionIdChange = new EventEmitter(); - - private readonly route = inject(ActivatedRoute); + twitchRedemptions = input.required({ + transform: toTwitchRedemptionArray + }); + twitchRedemptionId = model(); + search = input(false); errorMessageKeys: string[] = []; - constructor() { - this.route.data.subscribe(data => { - if (!data['twitchRedemptions']) - return; - - this.twitchRedemptions = data['twitchRedemptions']; - }); - } ngOnInit(): void { + if (this.twitchRedemptions() && this.twitchRedemptionId()) { + const redemption = this.twitchRedemptions()!.find(r => r.id == this.twitchRedemptionId()); + this.formControl.setValue(redemption); + } this.errorMessageKeys = Object.keys(this.errorMessages); - - if (!this.twitchRedemptionId || !this.twitchRedemptions) - return; - - const redemption = this.twitchRedemptions.find(r => r.id == this.twitchRedemptionId); - this.formControl.setValue(redemption); } get filteredRedemptions() { const value = this.formControl.value; - if (typeof (value) == 'string') { - return this.twitchRedemptions.filter(r => r.title.toLowerCase().includes(value.toLowerCase())); + if (this.twitchRedemptions() && typeof (value) == 'string') { + return this.twitchRedemptions()!.filter(r => r.title.toLowerCase().includes(value.toLowerCase())); } - return this.twitchRedemptions; + return this.twitchRedemptions(); } select(event: TwitchRedemption) { - this.twitchRedemptionIdChange.emit(event.id); + this.twitchRedemptionId.set(event.id); } input() { - if (this.search && typeof this.formControl.value == 'string') { - this.twitchRedemptionIdChange.emit(this.formControl.value); + if (this.search() && typeof this.formControl.value == 'string') { + this.twitchRedemptionId.set(this.formControl.value); } } blur() { + if (this.filteredRedemptions == null) + return; + if (!this.search && typeof this.formControl.value == 'string') { const name = this.formControl.value; const nameLower = name.toLowerCase(); - let newValue: TwitchRedemption | undefined = undefined; - const insenstiveActions = this.filteredRedemptions.filter(a => a.title.toLowerCase() == nameLower); - if (insenstiveActions.length > 1) { - const sensitiveAction = insenstiveActions.find(a => a.title == name); - newValue = sensitiveAction ?? undefined; - } else if (insenstiveActions.length == 1) { - newValue = insenstiveActions[0]; + let newValue: TwitchRedemption | undefined; + const filtered = this.filteredRedemptions?.filter(a => a.title.toLowerCase() == nameLower); + if (filtered.length > 1) { + newValue = filtered.find(a => a.title == name); + } else if (filtered.length == 1) { + newValue = filtered[0]; } if (newValue && newValue.id != this.formControl.value) { this.formControl.setValue(newValue); - this.twitchRedemptionIdChange.emit(newValue.id); + this.twitchRedemptionId.set(newValue.id); } else if (!newValue) - this.twitchRedemptionIdChange.emit(undefined); + this.twitchRedemptionId.set(undefined); } } diff --git a/src/app/shared/services/redeemable-action.service.ts b/src/app/shared/services/redeemable-action.service.ts index d23abe8..49579d2 100644 --- a/src/app/shared/services/redeemable-action.service.ts +++ b/src/app/shared/services/redeemable-action.service.ts @@ -1,6 +1,6 @@ import { inject, Injectable } from '@angular/core'; import { HermesClientService } from '../../hermes-client.service'; -import { map, Observable, of } from 'rxjs'; +import { map, merge, Observable, of, startWith } from 'rxjs'; import RedeemableAction from '../models/redeemable-action'; import EventService from './EventService'; @@ -12,6 +12,7 @@ export default class RedeemableActionService { private readonly events = inject(EventService); private data: RedeemableAction[] = []; private loaded = false; + changes$: Observable; create$: Observable | undefined; update$: Observable | undefined; delete$: Observable | undefined; @@ -21,6 +22,11 @@ export default class RedeemableActionService { 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.changes$ = merge(this.create$, this.update$, this.delete$) + .pipe( + startWith(null), + map(d => this.data.slice()), + ); this.create$?.subscribe(d => this.data.push(d.data)); this.update$?.subscribe(d => { diff --git a/src/app/shared/services/redemption.service.ts b/src/app/shared/services/redemption.service.ts index e15f4e2..d941381 100644 --- a/src/app/shared/services/redemption.service.ts +++ b/src/app/shared/services/redemption.service.ts @@ -1,7 +1,7 @@ import { inject, Injectable } from '@angular/core'; import Redemption from '../models/redemption'; import { HermesClientService } from '../../hermes-client.service'; -import { map, Observable, of } from 'rxjs'; +import { map, merge, Observable, of, startWith } from 'rxjs'; import EventService from './EventService'; @Injectable({ @@ -12,6 +12,7 @@ export default class RedemptionService { private readonly events = inject(EventService); private data: Redemption[] = [] private loaded = false; + changes$: Observable; create$: Observable | undefined; update$: Observable | undefined; delete$: Observable | undefined; @@ -20,6 +21,11 @@ export default class RedemptionService { this.create$ = this.client.filterByRequestType('create_redemption'); this.update$ = this.client.filterByRequestType('update_redemption'); this.delete$ = this.client.filterByRequestType('delete_redemption'); + this.changes$ = merge(this.create$, this.update$, this.delete$) + .pipe( + startWith(null), + map(d => this.data.slice()), + ); this.create$?.subscribe(d => this.data.push(d.data)); this.update$?.subscribe(d => { diff --git a/src/app/shared/services/twitch-redemption.service.ts b/src/app/shared/services/twitch-redemption.service.ts index 3301e7e..ffd694b 100644 --- a/src/app/shared/services/twitch-redemption.service.ts +++ b/src/app/shared/services/twitch-redemption.service.ts @@ -2,7 +2,7 @@ import { HttpClient } from '@angular/common/http'; import { inject, Injectable } from '@angular/core'; import { environment } from '../../../environments/environment'; import TwitchRedemption from '../models/twitch-redemption'; -import { catchError, EMPTY, Observable, of } from 'rxjs'; +import { catchError, EMPTY, of } from 'rxjs'; import EventService from './EventService'; @Injectable({ @@ -11,6 +11,7 @@ import EventService from './EventService'; export default class TwitchRedemptionService { private readonly http = inject(HttpClient); private readonly events = inject(EventService); + private twitchRedemptions: TwitchRedemption[] = []; private loaded = false; diff --git a/src/app/shared/transformers/twitch-redemption.transformer.ts b/src/app/shared/transformers/twitch-redemption.transformer.ts new file mode 100644 index 0000000..8ed21fa --- /dev/null +++ b/src/app/shared/transformers/twitch-redemption.transformer.ts @@ -0,0 +1,18 @@ +import TwitchRedemption from "../models/twitch-redemption"; +import { toDictStringKeyed } from "../utils/array.transformer"; + +export function toTwitchRedemptionDict(values: TwitchRedemption[] | null): { [key: string]: TwitchRedemption } { + if (!values) { + return {}; + } + + return toDictStringKeyed(values, r => r.id, r => r); +} + +export function toTwitchRedemptionArray(values: { [id: string]: TwitchRedemption } | null): TwitchRedemption[] { + if (!values) { + return []; + } + + return Object.values(values); +} \ No newline at end of file diff --git a/src/app/shared/utils/array.transformer.ts b/src/app/shared/utils/array.transformer.ts new file mode 100644 index 0000000..4b60423 --- /dev/null +++ b/src/app/shared/utils/array.transformer.ts @@ -0,0 +1,7 @@ +export function toDictStringKeyed(values: T[], keyGetter: { (k: T): string }, valueGetter: { (v: T): V }): { [key: string]: V } { + return Object.assign({}, ...values.map((t: T) => ({ [keyGetter(t)]: valueGetter(t) }))) +} + +export function toDictNumberKeyed(values: T[], keyGetter: { (k: T): number }, valueGetter: { (v: T): V }): { [key: number]: V } { + return Object.assign({}, ...values.map((t: T) => ({ [keyGetter(t)]: valueGetter(t) }))) +} \ No newline at end of file