1
1
mirror of https://github.com/bitgapp/eqMac.git synced 2024-11-22 22:32:17 +03:00

added event destroyers to all components with event listening

This commit is contained in:
Nodeful 2021-04-02 02:45:39 +03:00
parent 40416ee97c
commit f50c29e0a6
29 changed files with 332 additions and 262 deletions

View File

@ -38,9 +38,6 @@ import {
import {
EqualizersComponent
} from './sections/effects/equalizers/equalizers.component'
import {
ReverbComponent
} from './sections/effects/reverb/reverb.component'
import {
RecorderComponent
} from './sections/recorder/recorder.component'
@ -101,7 +98,6 @@ import { MatSnackBarModule } from '@angular/material/snack-bar'
BoosterComponent,
BalanceComponent,
EqualizersComponent,
ReverbComponent,
RecorderComponent,
OutputsComponent,
InputComponent,

View File

@ -10,4 +10,4 @@
</path>
</svg>
</div>
<eqm-label>Loading</eqm-label>
<eqm-label *ngIf="showText">{{text || 'Loading'}}</eqm-label>

View File

@ -1,4 +1,4 @@
import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core'
import { Component, ViewChild, ElementRef, AfterViewInit, Input } from '@angular/core'
@Component({
selector: 'eqm-loading',
@ -7,6 +7,8 @@ import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core'
})
export class LoadingComponent implements AfterViewInit {
@ViewChild('wave', { static: true }) wave: ElementRef
@Input() text?: string
@Input() showText = true
ngAfterViewInit () {
const path = this.wave.nativeElement

View File

@ -1,10 +1,10 @@
<div class="container" [ngStyle]="style">
<div #arrow
[hidden]="!showArrow"
class="arrow-container"
[ngStyle]="arrowStyle">
<div
class="arrow"></div>
[hidden]="!showArrow"
class="arrow-container"
[ngStyle]="arrowStyle"
>
<div class="arrow"></div>
</div>
<eqm-container class="tooltip" #tooltip>
<ng-content></ng-content>

View File

@ -42,6 +42,11 @@ export class TooltipComponent implements OnInit {
}
get style () {
if (!this.text?.length) {
return {
display: 'none'
}
}
let x = -999
let y = -999
const body = document.body

View File

@ -16,7 +16,13 @@ export class EffectService extends DataService {
return this.request({ method: 'POST', endpoint: '/enabled', data: { enabled } })
}
onEnabledChanged (callback: (enabled: boolean) => void) {
this.on('/enabled', ({ enabled }) => callback(enabled))
onEnabledChanged (callback: EffectEnabledChangedEventCallback) {
this.on('/enabled', callback)
}
offEnabledChanged (callback: EffectEnabledChangedEventCallback) {
this.off('/enabled', callback)
}
}
export type EffectEnabledChangedEventCallback = (data: { enabled: boolean }) => void

View File

@ -4,9 +4,10 @@ import {
Input,
EventEmitter,
Output,
ChangeDetectorRef
ChangeDetectorRef,
OnDestroy
} from '@angular/core'
import { AdvancedEqualizerService, AdvancedEqualizerPreset } from 'src/app/sections/effects/equalizers/advanced-equalizer/advanced-equalizer.service'
import { AdvancedEqualizerService, AdvancedEqualizerPreset, AdvancedEqualizerPresetsChangedEventCallback, AdvancedEqualizerSelectedPresetChangedEventCallback } from 'src/app/sections/effects/equalizers/advanced-equalizer/advanced-equalizer.service'
import { EqualizerComponent } from '../equalizer.component'
import { Options, CheckboxOption } from 'src/app/components/options/options.component'
import { TransitionService } from '../../../../services/transitions.service'
@ -17,7 +18,7 @@ import { ApplicationService } from '../../../../services/app.service'
templateUrl: './advanced-equalizer.component.html',
styleUrls: [ './advanced-equalizer.component.scss' ]
})
export class AdvancedEqualizerComponent extends EqualizerComponent implements OnInit {
export class AdvancedEqualizerComponent extends EqualizerComponent implements OnInit, OnDestroy {
@Input() enabled = true
public ShowDefaultPresetsCheckbox: CheckboxOption = {
@ -159,15 +160,25 @@ export class AdvancedEqualizerComponent extends EqualizerComponent implements On
this.ShowDefaultPresetsCheckbox.value = await this.service.getShowDefaultPresets()
}
private onPresetsChangedEventCallback: AdvancedEqualizerPresetsChangedEventCallback
private onSelectedPresetChangedEventCallback: AdvancedEqualizerSelectedPresetChangedEventCallback
protected setupEvents () {
this.service.onPresetsChanged(presets => {
this.onPresetsChangedEventCallback = presets => {
if (!presets) return
this.presets = presets
})
this.service.onSelectedPresetChanged(preset => {
}
this.service.onPresetsChanged(this.onPresetsChangedEventCallback)
this.onSelectedPresetChangedEventCallback = preset => {
this.selectedPreset = preset
this.setSelectedPresetsGains()
})
}
this.service.onSelectedPresetChanged(this.onSelectedPresetChangedEventCallback)
}
private destroyEvents () {
this.service.offPresetsChanged(this.onPresetsChangedEventCallback)
this.service.offSelectedPresetChanged(this.onSelectedPresetChangedEventCallback)
}
async selectPreset (preset: AdvancedEqualizerPreset) {
@ -248,4 +259,8 @@ export class AdvancedEqualizerComponent extends EqualizerComponent implements On
get globalGainScreenValue () {
return `${this.selectedPreset.gains.global > 0 ? '+' : ''}${(this.selectedPreset.gains.global.toFixed(1))}dB`
}
ngOnDestroy () {
this.destroyEvents()
}
}

View File

@ -80,11 +80,22 @@ export class AdvancedEqualizerService extends EqualizersService {
return this.request({ method: 'POST', endpoint: '/settings/show-default-presets', data: { show } })
}
onPresetsChanged (callback: (presets: AdvancedEqualizerPreset[]) => void) {
this.on('/presets', (presets) => callback(presets))
onPresetsChanged (callback: AdvancedEqualizerPresetsChangedEventCallback) {
this.on('/presets', callback)
}
onSelectedPresetChanged (callback: (preset: AdvancedEqualizerPreset) => void) {
this.on('/presets/selected', (preset) => callback(preset))
offPresetsChanged (callback: AdvancedEqualizerPresetsChangedEventCallback) {
this.off('/presets', callback)
}
onSelectedPresetChanged (callback: AdvancedEqualizerSelectedPresetChangedEventCallback) {
this.on('/presets/selected', callback)
}
offSelectedPresetChanged (callback: AdvancedEqualizerSelectedPresetChangedEventCallback) {
this.off('/presets/selected', callback)
}
}
export type AdvancedEqualizerPresetsChangedEventCallback = (presets: AdvancedEqualizerPreset[]) => void
export type AdvancedEqualizerSelectedPresetChangedEventCallback = (preset: AdvancedEqualizerPreset) => void

View File

@ -53,11 +53,22 @@ export class BasicEqualizerService extends EqualizersService {
return this.request({ method: 'DELETE', endpoint: '/presets', data: { ...preset } as any })
}
onPresetsChanged (callback: (presets: BasicEqualizerPreset[]) => void) {
this.on('/presets', (presets) => callback(presets))
onPresetsChanged (callback: BasicEqualizerPresetsChangedEventCallback) {
this.on('/presets', callback)
}
onSelectedPresetChanged (callback: (preset: BasicEqualizerPreset) => void) {
this.on('/presets/selected', (preset) => callback(preset))
offPresetsChanged (callback: BasicEqualizerPresetsChangedEventCallback) {
this.off('/presets', callback)
}
onSelectedPresetChanged (callback: BasicEqualizerSelectedPresetChangedEventCallback) {
this.on('/presets/selected', callback)
}
offSelectedPresetChanged (callback: BasicEqualizerSelectedPresetChangedEventCallback) {
this.off('/presets/selected', callback)
}
}
export type BasicEqualizerPresetsChangedEventCallback = (presets: BasicEqualizerPreset[]) => void
export type BasicEqualizerSelectedPresetChangedEventCallback = (preset: BasicEqualizerPreset) => void

View File

@ -1,5 +1,5 @@
import { Component, OnInit, EventEmitter, Output, ViewChild, Input, ChangeDetectorRef } from '@angular/core'
import { EqualizersService, EqualizerType } from './equalizers.service'
import { Component, OnInit, EventEmitter, Output, ViewChild, Input, ChangeDetectorRef, OnDestroy } from '@angular/core'
import { EqualizersService, EqualizersTypeChangedEventCallback, EqualizerType } from './equalizers.service'
import { BasicEqualizerComponent } from './basic-equalizer/basic-equalizer.component'
import { AdvancedEqualizerComponent } from './advanced-equalizer/advanced-equalizer.component'
import { EqualizerComponent } from './equalizer.component'
@ -8,6 +8,7 @@ import { MatDialog, MatDialogRef } from '@angular/material/dialog'
import { OptionsDialogComponent } from '../../../components/options-dialog/options-dialog.component'
import { EqualizerPreset } from './presets/equalizer-presets.component'
import { UIService } from '../../../services/ui.service'
import { EffectEnabledChangedEventCallback } from '../effect.service'
@Component({
selector: 'eqm-equalizers',
@ -15,7 +16,7 @@ import { UIService } from '../../../services/ui.service'
styleUrls: [ './equalizers.component.scss' ],
animations: [ FadeInOutAnimation ]
})
export class EqualizersComponent implements OnInit {
export class EqualizersComponent implements OnInit, OnDestroy {
@Input() animationDuration = 500
@Input() animationFps = 30
@ -75,14 +76,23 @@ export class EqualizersComponent implements OnInit {
this.enabled = await this.equalizersService.getEnabled()
}
private onEnabledChangedEventCallback: EffectEnabledChangedEventCallback
private onTypeChangedEventCallback: EqualizersTypeChangedEventCallback
protected setupEvents () {
this.equalizersService.onEnabledChanged(enabled => {
this.onEnabledChangedEventCallback = ({ enabled }) => {
this.enabled = enabled
})
}
this.equalizersService.onEnabledChanged(this.onEnabledChangedEventCallback)
this.equalizersService.onTypeChanged(type => {
this.onTypeChangedEventCallback = ({ type }) => {
this.type = type
})
}
this.equalizersService.onTypeChanged(this.onTypeChangedEventCallback)
}
private destroyEvents () {
this.equalizersService.offEnabledChanged(this.onEnabledChangedEventCallback)
this.equalizersService.offTypeChanged(this.onTypeChangedEventCallback)
}
setEnabled () {
@ -133,4 +143,8 @@ export class EqualizersComponent implements OnInit {
selectPreset (preset: EqualizerPreset) {
return this.activeEqualizer.selectPreset(preset)
}
ngOnDestroy () {
this.destroyEvents()
}
}

View File

@ -22,7 +22,13 @@ export class EqualizersService extends EffectService {
return this.request({ method: 'POST', endpoint: '/type', data: { type } })
}
onTypeChanged (callback: (type: EqualizerType) => void) {
this.on('/type', ({ type }) => callback(type))
onTypeChanged (callback: EqualizersTypeChangedEventCallback) {
this.on('/type', callback)
}
offTypeChanged (callback: EqualizersTypeChangedEventCallback) {
this.off('/type', callback)
}
}
export type EqualizersTypeChangedEventCallback = (data: { type: EqualizerType }) => void

View File

@ -1,22 +0,0 @@
<div class="reverb" fxLayout="column" fxFill fxLayoutAlign="center center">
<div fxLayout="row" [style.padding]="'0 5px'" fxLayoutAlign="space-between center" fxFill>
<eqm-toggle class="on-off" [(state)]="enabled" (stateChange)="setEnabled()"></eqm-toggle>
<eqm-label>Reverb</eqm-label>
<eqm-icon (click)="toggleVisibility()" [name]="hide ? 'show' : 'hide'"></eqm-icon>
</div>
<div fxFill class="reverb-controls" [fxHide]="hide">
<div fxLayout="row" fxFill fxLayoutAlign="space-around center">
<eqm-knob [min]="0" [max]="1" [(value)]="dryWetMix" (valueChange)="setMix()" [disabled]="enabled"></eqm-knob>
<div fxLayout="column" fxFlex="70" fxLayoutAlign="space-between start" fxLayoutGap="10px">
<div fxLayout="row" fxFill fxLayoutAlign="space-around start" fxLayoutGap="20px">
<eqm-label>Preset</eqm-label>
<eqm-dropdown [items]="presets" [labelParam]="'name'" [(selectedItem)]="selectedPreset"></eqm-dropdown>
</div>
<div fxLayout="row" fxFill fxLayoutGap="20px">
<eqm-label>Dry/Wet Mix</eqm-label>
<eqm-value-screen>{{formatDryWetMixValue()}}%</eqm-value-screen>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,7 +0,0 @@
.reverb {
position: relative;
.reverb-controls{
// filter: grayscale(100%);
}
}

View File

@ -1,89 +0,0 @@
import {
Component,
OnInit,
EventEmitter,
Output
} from '@angular/core'
import {
ReverbService
} from './reverb.service'
@Component({
selector: 'eqm-reverb',
templateUrl: './reverb.component.html',
styleUrls: [ './reverb.component.scss' ]
})
export class ReverbComponent implements OnInit {
presets = []
dryWetMix = 0.5
enabled = true
selectedPreset = null
hide = false
@Output() visibilityToggled = new EventEmitter()
constructor (public reverbService: ReverbService) {}
ngOnInit () {
this.getPresets()
this.sync()
this.setupEvents()
}
protected setupEvents () {
this.reverbService.onEnabledChanged(enabled => {
this.enabled = enabled
})
this.reverbService.onMixChanged(mix => {
this.dryWetMix = mix
})
this.reverbService.onPresetChanged(preset => {
this.selectedPreset = preset
})
}
async sync () {
if (this.presets.length === 0) {
this.getPresets()
}
this.getSelectedPreset()
this.getMix()
this.getEnabled()
}
async getPresets () {
this.presets = await this.reverbService.getPresets()
}
async getSelectedPreset () {
const selectedPresetId = await this.reverbService.getSelectedPresetId()
this.selectedPreset = this.presets.find(preset => preset.id === selectedPresetId)
}
async getMix () {
this.dryWetMix = await this.reverbService.getMix()
}
setMix () {
this.reverbService.setMix(this.dryWetMix)
}
async getEnabled () {
this.enabled = await this.reverbService.getEnabled()
}
setEnabled () {
this.reverbService.setEnabled(this.enabled)
}
formatDryWetMixValue () {
return Math.round(this.dryWetMix * 100)
}
toggleVisibility () {
this.hide = !this.hide
this.visibilityToggled.emit()
}
}

View File

@ -1,39 +0,0 @@
import { Injectable } from '@angular/core'
import { EffectService } from '../effect.service'
@Injectable({
providedIn: 'root'
})
export class ReverbService extends EffectService {
route = `${this.route}/reverb`
async getMix () {
const resp = await this.request({ method: 'GET', endpoint: '/mix' })
return resp.mix
}
setMix (mix) {
return this.request({ method: 'POST', endpoint: '/mix', data: { mix } })
}
async getPresets () {
return this.request({ method: 'GET', endpoint: '/presets' })
}
async getSelectedPresetId () {
const resp = await this.request({ method: 'GET', endpoint: '/preset' })
return resp.id
}
setPreset (id) {
return this.request({ method: 'POST', endpoint: '/preset', data: { id } })
}
onMixChanged (callback: (mix: number) => void) {
this.on(`${this.route}/mix`, ({ mix }) => callback(mix))
}
onPresetChanged (callback: (id: number) => void) {
this.on(`${this.route}/preset`, ({ preset }) => callback(preset))
}
}

View File

@ -50,7 +50,12 @@ export class OutputsComponent implements OnInit {
}
setupEventListeners () {
this.service.onChanged(() => this.sync())
this.service.onDevicesChanged(() => this.sync())
this.service.onSelectedChanged(this.sync.bind(this))
this.service.onDevicesChanged(this.sync.bind(this))
}
destroyEvents () {
this.service.offSelectedChanged(this.sync)
this.service.offDevicesChanged(this.sync)
}
}

View File

@ -40,11 +40,22 @@ export class OutputsService extends DataService {
return this.request({ method: 'POST', endpoint: '/selected', data: { ...output } })
}
onChanged (callback: (id: number) => void) {
this.on('/selected', ({ id }) => callback(id))
onSelectedChanged (callback: OutputsSelectedChangedEventCallback) {
this.on('/selected', callback)
}
onDevicesChanged (callback: (devices: Output[]) => void) {
this.on('/devices', devices => callback(devices))
offSelectedChanged (callback: OutputsSelectedChangedEventCallback) {
this.off('/selected', callback)
}
onDevicesChanged (callback: OutputsDevicesChangedEventCallback) {
this.on('/devices', callback)
}
offDevicesChanged (callback: OutputsDevicesChangedEventCallback) {
this.off('/devices', callback)
}
}
export type OutputsSelectedChangedEventCallback = (data: { id: number }) => void
export type OutputsDevicesChangedEventCallback = (devices: Output[]) => void

View File

@ -7,6 +7,8 @@ import {
UtilitiesService
} from '../../../services/utilities.service'
import {
FilePlayingChangedEventCallback,
FileProgressChangedEventCallback,
FileService
} from './file.service'
import { ApplicationService } from '../../../services/app.service'
@ -49,6 +51,7 @@ export class FileComponent implements OnInit, OnDestroy {
if (this.progressProjectionInterval) {
clearInterval(this.progressProjectionInterval)
}
this.destroyEvents()
}
public setDefaultMeta () {
@ -151,13 +154,23 @@ export class FileComponent implements OnInit, OnDestroy {
}, 10)
}
protected setupEvents () {
this.fileService.onPlayingChanged(playing => {
this.playing = playing
})
private onPlayingChangedEventCallback: FilePlayingChangedEventCallback
private onProgressChangedEventCallback: FileProgressChangedEventCallback
this.fileService.onProgressChanged(progress => {
protected setupEvents () {
this.onPlayingChangedEventCallback = ({ playing }) => {
this.playing = playing
}
this.fileService.onPlayingChanged(this.onPlayingChangedEventCallback)
this.onProgressChangedEventCallback = ({ progress }) => {
this.progress = progress
})
}
this.fileService.onProgressChanged(this.onProgressChangedEventCallback)
}
private destroyEvents () {
this.fileService.offPlayingChanged(this.onPlayingChangedEventCallback)
this.fileService.offProgressChanged(this.onProgressChangedEventCallback)
}
}

View File

@ -4,7 +4,6 @@ import { SourceService } from '../source.service'
@Injectable({
providedIn: 'root'
})
@Injectable()
export class FileService extends SourceService {
route = `${this.route}/file`
@ -45,11 +44,22 @@ export class FileService extends SourceService {
return resp.playing
}
onPlayingChanged (callback: (playing: boolean) => void) {
this.on('/playing', ({ playing }) => callback(playing))
onPlayingChanged (callback: FilePlayingChangedEventCallback) {
this.on('/playing', callback)
}
onProgressChanged (callback: (progress: number) => void) {
this.on('/progress', ({ progress }) => callback(progress))
offPlayingChanged (callback: FilePlayingChangedEventCallback) {
this.off('/playing', callback)
}
onProgressChanged (callback: FileProgressChangedEventCallback) {
this.on('/progress', callback)
}
offProgressChanged (callback: FileProgressChangedEventCallback) {
this.off('/progress', callback)
}
}
export type FilePlayingChangedEventCallback = (data: { playing: boolean }) => void
export type FileProgressChangedEventCallback = (data: { progress: number }) => void

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core'
import { InputService } from './input.service'
import { Component, OnDestroy, OnInit } from '@angular/core'
import { InputDeviceChangedEventCallback, InputService } from './input.service'
export interface InputDevice {
deviceId: number
@ -12,7 +12,7 @@ export interface InputDevice {
templateUrl: './input.component.html',
styleUrls: [ './input.component.scss' ]
})
export class InputComponent implements OnInit {
export class InputComponent implements OnInit, OnDestroy {
constructor (public inputService: InputService) { }
devices: InputDevice[] = []
@ -38,10 +38,16 @@ export class InputComponent implements OnInit {
await this.inputService.getDevice()
}
private onDeviceChangedEventCallback: InputDeviceChangedEventCallback
protected setupEvents () {
this.inputService.onDeviceChanged((deviceId) => {
this.onDeviceChangedEventCallback = ({ deviceId }) => {
this.setDeviceById(deviceId)
})
}
this.inputService.onDeviceChanged(this.onDeviceChangedEventCallback)
}
private destroyEvents () {
this.inputService.offDeviceChanged(this.onDeviceChangedEventCallback)
}
deviceSelected (device) {
@ -61,4 +67,8 @@ export class InputComponent implements OnInit {
}
this.selectedDevice = device
}
ngOnDestroy () {
this.destroyEvents()
}
}

View File

@ -20,7 +20,13 @@ export class InputService extends SourceService {
return this.request({ method: 'POST', endpoint: '/device', data: { deviceId } })
}
onDeviceChanged (callback: (deviceId: number) => void) {
this.on(({ deviceId }) => callback(deviceId))
onDeviceChanged (callback: InputDeviceChangedEventCallback) {
this.on(callback)
}
offDeviceChanged (callback: InputDeviceChangedEventCallback) {
this.off(callback)
}
}
export type InputDeviceChangedEventCallback = (data: { deviceId: number }) => void

View File

@ -1,12 +1,12 @@
import { Component, OnInit } from '@angular/core'
import { SourceService, SourceType } from './source.service'
import { Component, OnDestroy, OnInit } from '@angular/core'
import { SourceChangedEventCallback, SourceService, SourceType } from './source.service'
@Component({
selector: 'eqm-source',
templateUrl: './source.component.html',
styleUrls: [ './source.component.scss' ]
})
export class SourceComponent implements OnInit {
export class SourceComponent implements OnInit, OnDestroy {
source: SourceType = 'File'
constructor (public sourceService: SourceService) { }
@ -14,10 +14,16 @@ export class SourceComponent implements OnInit {
this.setupEvents()
}
private onSourceChangedEventCallback: SourceChangedEventCallback
protected setupEvents () {
this.sourceService.onSourceChanged(source => {
this.onSourceChangedEventCallback = ({ source }) => {
this.source = source
})
}
this.sourceService.onSourceChanged(this.onSourceChangedEventCallback)
}
private destroyEvents () {
this.sourceService.offSourceChanged(this.onSourceChangedEventCallback)
}
async setSource (source: SourceType) {
@ -26,4 +32,8 @@ export class SourceComponent implements OnInit {
this.sourceService.setSource(this.source)
}
}
ngOnDestroy () {
this.destroyEvents()
}
}

View File

@ -18,7 +18,13 @@ export class SourceService extends DataService {
return this.request({ method: 'POST', endpoint: '', data: { source } })
}
onSourceChanged (callback: (source: SourceType) => void) {
this.on(({ source }) => callback(source))
onSourceChanged (callback: SourceChangedEventCallback) {
this.on(callback)
}
offSourceChanged (callback: SourceChangedEventCallback) {
this.off(callback)
}
}
export type SourceChangedEventCallback = (data: { source: SourceType }) => void

View File

@ -1,16 +1,17 @@
import { Component, OnInit, Input } from '@angular/core'
import { BalanceService } from './balance.service'
import { Component, OnInit, Input, OnDestroy } from '@angular/core'
import { BalanceChangedEventCallback, BalanceService } from './balance.service'
import { ApplicationService } from '../../../../services/app.service'
import { KnobValueChangedEvent } from '../../../../modules/eqmac-components/components/knob/knob.component'
import { FlatSliderValueChangedEvent } from '../../../../modules/eqmac-components/components/flat-slider/flat-slider.component'
import { UIService } from '../../../../services/ui.service'
import { Subscription } from 'rxjs'
@Component({
selector: 'eqm-balance',
templateUrl: './balance.component.html',
styleUrls: [ './balance.component.scss' ]
})
export class BalanceComponent implements OnInit {
export class BalanceComponent implements OnInit, OnDestroy {
balance = 0
@Input() animationDuration = 500
@Input() animationFps = 30
@ -40,15 +41,24 @@ export class BalanceComponent implements OnInit {
this.replaceKnobsWithSliders = uiSettings.replaceKnobsWithSliders
}
private onBalanceChangedEventCallback: BalanceChangedEventCallback
private onUISettingsChangedSubscription: Subscription
protected setupEvents () {
this.balanceService.onBalanceChanged((balance) => {
this.onBalanceChangedEventCallback = ({ balance }) => {
this.balance = balance
})
this.ui.settingsChanged.subscribe(uiSettings => {
}
this.balanceService.onBalanceChanged(this.onBalanceChangedEventCallback)
this.onUISettingsChangedSubscription = this.ui.settingsChanged.subscribe(uiSettings => {
this.replaceKnobsWithSliders = uiSettings.replaceKnobsWithSliders
})
}
protected destroyEvents () {
this.balanceService.offBalanceChanged(this.onBalanceChangedEventCallback)
this.onUISettingsChangedSubscription?.unsubscribe()
}
async getBalance () {
this.balance = await this.balanceService.getBalance()
}
@ -62,4 +72,8 @@ export class BalanceComponent implements OnInit {
this.app.haptic()
}
}
ngOnDestroy () {
this.destroyEvents()
}
}

View File

@ -14,7 +14,13 @@ export class BalanceService extends VolumeService {
return this.request({ method: 'POST', endpoint: '/balance', data: { balance, transition } })
}
onBalanceChanged (callback: (balance: number) => void) {
this.on(({ balance }) => callback(balance))
onBalanceChanged (callback: BalanceChangedEventCallback) {
this.on(callback)
}
offBalanceChanged (callback: BalanceChangedEventCallback) {
this.off(callback)
}
}
export type BalanceChangedEventCallback = (data: { balance: number }) => void

View File

@ -2,19 +2,21 @@ import {
Component,
OnInit,
Input,
ChangeDetectorRef
ChangeDetectorRef,
OnDestroy
} from '@angular/core'
import { BoosterService } from './booster.service'
import { BoosterGainChangedEventCallback, BoosterService } from './booster.service'
import { ApplicationService } from '../../../../services/app.service'
import { UIService } from '../../../../services/ui.service'
import { UIService, UISettings } from '../../../../services/ui.service'
import { Subscription } from 'rxjs'
@Component({
selector: 'eqm-booster',
templateUrl: './booster.component.html',
styleUrls: [ './booster.component.scss' ]
})
export class BoosterComponent implements OnInit {
export class BoosterComponent implements OnInit, OnDestroy {
gain = 1
replaceKnobsWithSliders = false
@Input() hide = false
@ -40,18 +42,27 @@ export class BoosterComponent implements OnInit {
public ignoreUpdates = false
public ignoreUpdatesDebouncer: NodeJS.Timer
private onGainChangedEventCallback: BoosterGainChangedEventCallback
private onUISettingsChangedEventSubscription: Subscription
protected setupEvents () {
this.boosterService.onGainChanged(gain => {
this.onGainChangedEventCallback = ({ gain }) => {
if (!this.ignoreUpdates) {
this.gain = gain
this.changeRef.detectChanges()
}
})
this.ui.settingsChanged.subscribe(uiSettings => {
}
this.boosterService.onGainChanged(this.onGainChangedEventCallback)
this.onUISettingsChangedEventSubscription = this.ui.settingsChanged.subscribe(uiSettings => {
this.replaceKnobsWithSliders = uiSettings.replaceKnobsWithSliders
})
}
protected destroyEvents () {
this.boosterService.offGainChanged(this.onGainChangedEventCallback)
this.onUISettingsChangedEventSubscription.unsubscribe()
}
async syncUISettings () {
const uiSettings = await this.ui.getSettings()
this.replaceKnobsWithSliders = uiSettings.replaceKnobsWithSliders
@ -76,4 +87,8 @@ export class BoosterComponent implements OnInit {
this.app.haptic()
}
}
ngOnDestroy () {
this.destroyEvents()
}
}

View File

@ -15,7 +15,13 @@ export class BoosterService extends VolumeService {
this.request({ method: 'POST', data: { gain, transition } })
}
onGainChanged (callback: (gain: number) => void) {
this.on(({ gain }) => callback(gain))
onGainChanged (callback: BoosterGainChangedEventCallback) {
this.on(callback)
}
offGainChanged (callback: BoosterGainChangedEventCallback) {
this.off(callback)
}
}
export type BoosterGainChangedEventCallback = (data: { gain: number }) => void

View File

@ -17,9 +17,12 @@ type CallHandlerCallback = (data?: BridgeResponseData) => void
type RegisterHandler = (handler: string, cb: RegisterHandlerCallback) => void
type RegisterHandlerCallback = (data: JSONData, cb: (data?: BridgeResponseData) => void) => void
type EventHandler = (data: JSONData, res: BridgeResponse) => void | Promise<void>
interface JSBridge {
callHandler: CallHandler
registerHandler: RegisterHandler
disableJavscriptAlertBoxSafetyTimeout: () => void
}
/**
@ -32,6 +35,10 @@ interface JSBridge {
export class BridgeService {
public static bridgeLoadTimeout = 10000
public static bridgeLoadPromise: Promise<JSBridge> = null
private static didSpeedUp = false
private static readonly handlers: {
[event: string]: EventHandler[]
} = {}
public get bridge () {
if (BridgeService.bridgeLoadPromise) {
@ -66,6 +73,10 @@ export class BridgeService {
async call (handler: string, data?: JSONData): Promise<any> {
return new Promise(async (resolve, reject) => {
const bridge = await this.bridge
if (!BridgeService.didSpeedUp) {
BridgeService.didSpeedUp = true
bridge.disableJavscriptAlertBoxSafetyTimeout()
}
bridge.callHandler(handler, data, res => {
const err = res.error
return err ? reject(new Error(err)) : resolve(res.data)
@ -73,22 +84,48 @@ export class BridgeService {
})
}
async on (event: string, handler: (data: JSONData, res: BridgeResponse) => void | Promise<void>) {
async on (event: string, handler: EventHandler) {
const bridge = await this.bridge
bridge.registerHandler(event, async (data, cb) => {
const handleError = (err: string | Error) => {
console.error(err)
// eslint-disable-next-line @typescript-eslint/no-base-to-string
cb({ error: err.toString() })
}
try {
await handler(data, {
send: (data) => cb({ data }),
error: (err) => handleError(err)
})
} catch (err) {
handleError(err)
}
})
let shouldRegister = false
if (!(event in BridgeService.handlers)) {
BridgeService.handlers[event] = []
shouldRegister = true
}
BridgeService.handlers[event].push(handler)
if (shouldRegister) {
bridge.registerHandler(event, async (data, cb) => {
const handleError = (err: string | Error) => {
console.error(err)
// eslint-disable-next-line @typescript-eslint/no-base-to-string
cb({ error: err.toString() })
}
for (const handler of BridgeService.handlers[event]) {
try {
await handler(data, {
send: (data) => cb({ data }),
error: (err) => handleError(err)
})
} catch (err) {
handleError(err)
}
}
})
}
}
async off (event: string, handler: EventHandler) {
if (!BridgeService.handlers[event]?.length) {
console.error(`Trying to unsubscribe from event: "${event}" when there are no handlers registered`)
return
}
const index = BridgeService.handlers[event]?.indexOf(handler)
if (index > -1) {
BridgeService.handlers[event].splice(index, 1)
} else {
console.error(`Trying to unsubscribe from event: "${event}" with a handler that is not registered`)
}
}
}

View File

@ -31,11 +31,23 @@ export class DataService {
return resp
}
private normalizeEventCallback (eventOrCallback: string | EventCallback, callback?: EventCallback) {
const event = typeof eventOrCallback === 'string' ? eventOrCallback : ''
callback = typeof eventOrCallback === 'function' ? eventOrCallback : callback
return { event, callback }
}
async on (callback: EventCallback)
async on (event: string, callback: EventCallback)
async on (eventOrCallback: string | EventCallback, callback?: EventCallback) {
const eventName = typeof eventOrCallback === 'string' ? eventOrCallback : ''
callback = typeof eventOrCallback === 'function' ? eventOrCallback : callback
this.bridge.on(`${this.route}${eventName}`, callback)
async on (eventOrCallback: string | EventCallback, cb?: EventCallback) {
const { event, callback } = this.normalizeEventCallback(eventOrCallback, cb)
this.bridge.on(`${this.route}${event}`, callback)
}
async off (callback: EventCallback)
async off (event: string, callback: EventCallback)
async off (eventOrCallback: string | EventCallback, cb?: EventCallback) {
const { event, callback } = this.normalizeEventCallback(eventOrCallback, cb)
this.bridge.off(`${this.route}${event}`, callback)
}
}