diff --git a/native/app/Source/DataBus/DataBus.swift b/native/app/Source/DataBus/DataBus.swift index 9ecd7cb..6e2213b 100644 --- a/native/app/Source/DataBus/DataBus.swift +++ b/native/app/Source/DataBus/DataBus.swift @@ -15,10 +15,14 @@ enum DataMethod: String { case DELETE = "DELETE" } +typealias DataBusHandler = (JSON?, BridgeResponse) throws -> JSON? +typealias DataBusMiddlewareHandler = (JSON?) throws -> Void + class DataBus { private var childBuses: [DataBus] = [] private let route: String! private var bridge: Bridge! + private var middlewares: [String: [DataBusMiddlewareHandler]] = [:] required init (route: String, bridge: Bridge) { self.route = route @@ -30,13 +34,20 @@ class DataBus { } func send (to path: String, data: JSON) { - _ = self.bridge.call(handler: self.route + path, data: data) + self.bridge.call(handler: self.route + path, data: data) } - func on (_ method: DataMethod, _ path: String, _ handler: @escaping (JSON?, BridgeResponse) throws -> JSON?) { + func on (_ method: DataMethod, _ path: String, _ handler: @escaping DataBusHandler) { let event = "\(method.rawValue) \(self.route!)\(path)" self.bridge.on(event: event) { (data, res) in do { + let middlewareHandlers = self.getMiddlewareHandlersMatching(path: path) + + if (middlewareHandlers.count > 0) { + for middlewareHandler in middlewareHandlers { + try middlewareHandler(data) + } + } if let resp = try handler(data, res) { res.send(resp) } @@ -46,6 +57,32 @@ class DataBus { } } + private func getMiddlewareHandlersMatching (path: String) -> [DataBusMiddlewareHandler] { + var handlers: [DataBusMiddlewareHandler] = [] + let keys = middlewares.map { String($0.key) } + let matchingKeys = keys.filter { key in + return path.hasPrefix(key) + } + + for (_, key) in matchingKeys.enumerated() { + if let matchingHandlers = middlewares[key] { + for matchingHandler in matchingHandlers { + handlers.append(matchingHandler) + } + } + } + + return handlers + } + + func onAll (_ path: String, _ handler: @escaping DataBusMiddlewareHandler) { + if middlewares[path] == nil { + middlewares[path] = [] + } + + middlewares[path]!.append(handler) + } + func add (_ route: String, _ Bus: DataBus.Type) { childBuses.append(Bus.init( route: self.route + route, diff --git a/native/app/Source/DataBus/DataBusEvent.swift b/native/app/Source/DataBus/DataBusEvent.swift deleted file mode 100644 index 5051302..0000000 --- a/native/app/Source/DataBus/DataBusEvent.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// ServerEvent.swift -// eqMac -// -// Created by Romans Kisils on 24/04/2019. -// Copyright © 2019 Romans Kisils. All rights reserved. -// - -import Foundation -import SwiftyJSON - -struct DataBusEvent: Codable { - let route: String - let event: String - let data: JSON -} diff --git a/native/app/eqMac.xcodeproj/project.pbxproj b/native/app/eqMac.xcodeproj/project.pbxproj index 7be9a6a..871c82d 100644 --- a/native/app/eqMac.xcodeproj/project.pbxproj +++ b/native/app/eqMac.xcodeproj/project.pbxproj @@ -61,7 +61,6 @@ E057A637227032EC0019D13C /* EngineDataBus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E057A636227032EC0019D13C /* EngineDataBus.swift */; }; E057A639227038B80019D13C /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E057A638227038B80019D13C /* Settings.swift */; }; E057A63B22703F590019D13C /* BasicEqualizerDataBus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E057A63A22703F590019D13C /* BasicEqualizerDataBus.swift */; }; - E057A641227043EF0019D13C /* DataBusEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = E057A640227043EF0019D13C /* DataBusEvent.swift */; }; E057A6472270582C0019D13C /* EqualizersDataBus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E057A6462270582C0019D13C /* EqualizersDataBus.swift */; }; E057A64922710F2B0019D13C /* AdvancedEqualizerDataBus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E057A64822710F2B0019D13C /* AdvancedEqualizerDataBus.swift */; }; E057A64D2271A6DD0019D13C /* VolumeDataBus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E057A64C2271A6DD0019D13C /* VolumeDataBus.swift */; }; @@ -178,7 +177,6 @@ E057A636227032EC0019D13C /* EngineDataBus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EngineDataBus.swift; sourceTree = ""; }; E057A638227038B80019D13C /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; E057A63A22703F590019D13C /* BasicEqualizerDataBus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicEqualizerDataBus.swift; sourceTree = ""; }; - E057A640227043EF0019D13C /* DataBusEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBusEvent.swift; sourceTree = ""; }; E057A6462270582C0019D13C /* EqualizersDataBus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EqualizersDataBus.swift; sourceTree = ""; }; E057A64822710F2B0019D13C /* AdvancedEqualizerDataBus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedEqualizerDataBus.swift; sourceTree = ""; }; E057A64C2271A6DD0019D13C /* VolumeDataBus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumeDataBus.swift; sourceTree = ""; }; @@ -352,7 +350,6 @@ E036BCE8235B09CB001F057B /* DataBus */ = { isa = PBXGroup; children = ( - E057A640227043EF0019D13C /* DataBusEvent.swift */, E036BCE623598772001F057B /* DataBus.swift */, ); path = DataBus; @@ -875,7 +872,6 @@ E0493D0B21F222D300A8FD3D /* EventInterceptor.swift in Sources */, E044AB7621ED27E4000C41A1 /* Equalizer.swift in Sources */, E01B1BB5235E452D00CC58E5 /* Sources.swift in Sources */, - E057A641227043EF0019D13C /* DataBusEvent.swift in Sources */, E090AD5A20A972DE003BE5FF /* BasicEqualizer.swift in Sources */, E01F2F5F2034A188005123BE /* ArrayExtensions.swift in Sources */, E0DDF3FA2188390300B37E77 /* Alert.swift in Sources */, diff --git a/ui/src/app/services/data.service.ts b/ui/src/app/services/data.service.ts index 156908f..e294ebb 100644 --- a/ui/src/app/services/data.service.ts +++ b/ui/src/app/services/data.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core' import { Bridge } from './bridge.service' +import { ToastService } from './toast.service' export type JSONEncodable = null | boolean | number | string | JSONData export interface JSONData { @@ -18,10 +19,21 @@ type EventCallback = (data?: any) => void export class DataService { route = '' + constructor (public toast?: ToastService) {} + async request (opts: RequestOptions): Promise { if (opts.endpoint && opts.endpoint[0] !== '/') opts.endpoint = `/${opts.endpoint}` const args: [string, any?] = [ `${opts.method} ${this.route}${opts.endpoint || ''}`, opts.data ] - const resp = await Bridge.call(...args) + let resp + try { + resp = await Bridge.call(...args) + } catch (err) { + this.toast.show({ + message: err, + type: 'warning' + }) + throw err + } return resp } diff --git a/ui/src/app/services/toast.service.ts b/ui/src/app/services/toast.service.ts index 16146a7..1308da6 100644 --- a/ui/src/app/services/toast.service.ts +++ b/ui/src/app/services/toast.service.ts @@ -1,23 +1,36 @@ import { Injectable } from '@angular/core' import { MatSnackBar } from '@angular/material/snack-bar' +interface ToastShowOptions { + message: string + type?: 'success' | 'warning' | 'normal' + action?: string + duration?: number +} + @Injectable({ providedIn: 'root' }) export class ToastService { constructor (private readonly snackBar: MatSnackBar) {} - open ({ message, action }: { - message: string - action?: string - }) { - action = action || 'Hide' - const duration = 1000 + show ({ message, action, type, duration }: ToastShowOptions) { + action ||= 'Hide' + type ||= 'normal' + duration ??= 2000 + duration <= 0 && (duration = 2000) + const [ bgClass, textClass ] = (() => { + switch (type) { + case 'success': return [ 'accent-bg', 'dark' ] + case 'warning': return [ 'warning-bg', 'dark' ] + default: return [ 'dark-bg', 'light' ] + } + })() const toast = this.snackBar.open(message, action, { horizontalPosition: 'center', verticalPosition: 'top', - duration: 10000, - panelClass: [ 'dark-bg', 'light' ] + duration, + panelClass: [ bgClass, textClass ] }) setTimeout(() => { toast?.dismiss()