import {collect} from "../collection/Collection"; import {InvalidJSONStateError, JSONState, Rehydratable} from "./Rehydratable"; import {Pipe} 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 !!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[]) { 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) { 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) { 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) { 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 ( !state.hasOwnProperty(key) ) continue; const set = state[key] if ( !(Array.isArray(set) && set.every(x => typeof x === 'string')) ) { all = false; break; } } if ( all ) { // @ts-ignore this.messages = state; } } throw new InvalidJSONStateError('Invalid message state object.', { state }); } toJSON() { return this.messages } toString() { return JSON.stringify(this) } /** * Get a new Pipe object wrapping this instance. */ pipe(): Pipe { return Pipe.wrap(this) } }