You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
133 lines
3.6 KiB
133 lines
3.6 KiB
3 years ago
|
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<string[]>(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<string>(this.all(forKey)).unique<string>().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<JSONState> {
|
||
|
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<Messages> {
|
||
|
return Pipe.wrap<Messages>(this)
|
||
|
}
|
||
|
}
|