import {collect} from '../collection/Collection' import {InvalidJSONStateError, JSONState, Rehydratable} from './Rehydratable' import {Pipeline} from './Pipe' /** * A class for building and working with messages grouped by keys. */ export class Messages implements Rehydratable { protected messages: {[key: string]: string[]} = {} /** * @param [allMessages] an initial array of messages to put in the "all" key */ constructor(allMessages?: string[]) { if ( allMessages ) { this.messages.all = allMessages } } /** All group keys of messages. */ public keys(): string[] { return Object.keys(this.messages) } /** Add a message to a group. */ public put(key: string, message: string): this { if ( !this.messages[key] ) { this.messages[key] = [] } this.messages[key].push(message) return this } /** Returns true if the given group has the message. */ public has(key: string, message: string): boolean { return Boolean(this.messages[key]?.includes(message)) } /** * Returns true if any messages are found. * @param [forKeys] if provided, only search the given keys */ public any(forKeys?: string[]): boolean { if ( forKeys ) { return forKeys.map(key => this.messages[key]) .filter(Boolean) .some((bag: string[]) => bag.length) } return Object.values(this.messages).some((bag: string[]) => bag.length) } /** * Returns the first message. * @param [forKey] if provided, only search the given key */ public first(forKey?: string): string | undefined { if ( !forKey ) { forKey = Object.keys(this.messages)[0] } if ( forKey && this.messages[forKey].length ) { return this.messages[forKey][0] } } /** * Return all messages in a flat array. * @param [forKey] if provided, only search the given key */ public all(forKey?: string): string[] { if ( forKey ) { return this.messages[forKey] || [] } return collect(Object.values(this.messages)).collapse() .all() as string[] } /** * Flat array of only distinct messages. * @param [forKey] if provided, only search the given key */ public unique(forKey?: string): string[] { return collect(this.all(forKey)).unique() .all() } /** * Get the total number of messages. * @param [forKey] if provided, only search the given key */ public count(forKey?: string): number { return this.all(forKey).length } async dehydrate(): Promise { return this.messages } rehydrate(state: JSONState): void { if ( typeof state === 'object' && !Array.isArray(state) ) { let all = true for ( const key in state ) { if ( !Object.prototype.hasOwnProperty.call(state, key) ) { continue } const set = state[key] if ( !(Array.isArray(set) && set.every(x => typeof x === 'string')) ) { all = false break } } if ( all ) { this.messages = state as any } } throw new InvalidJSONStateError('Invalid message state object.', { state }) } toJSON(): {[key: string]: string[]} { return this.messages } toString(): string { return JSON.stringify(this.toJSON()) } /** * Return a new Pipe of this collection. */ pipeTo(pipeline: Pipeline): TOut { return pipeline.apply(this) } /** Build and apply a pipeline. */ pipe(builder: (pipeline: Pipeline) => Pipeline): TOut { return builder(Pipeline.id()).apply(this) } }