import { Scope, EventStream, EventStreamSeed, isEventStreamSeed, isPropertySeed, Property, PropertySeed, isScope, } from "./abstractions" import { merge } from "./merge" import { scan } from "./scan" import { map } from "./map" import { applyScopeMaybe } from "./applyscope" import { toString } from "./tostring" import { rename } from "./util" export type UpdateTrigger = EventStream | EventStreamSeed type UpdateParam = UpdateTrigger | Property /** * [Update](#update) pattern consisting of a single EventStream and a accumulator function. */ export type UpdatePattern1 = [ UpdateTrigger, O | ((acc: O, a: I1) => O) ] /** * [Update](#update) pattern consisting of 2 Observables and an accumulrator function. */ export type UpdatePattern2 = [ UpdateTrigger, Property, O | ((acc: O, a: I1, b: I2) => O) ] /** * [Update](#update) pattern consisting of 3 Observables and an accumulrator function. */ export type UpdatePattern3 = [ UpdateTrigger, Property, Property, O | ((acc: O, a: I1, b: I2, c: I3) => O) ] /** * [Update](#update) pattern consisting of 4 Observables and an accumulrator function. */ export type UpdatePattern4 = [ UpdateTrigger, Property, Property, Property, O | ((acc: O, a: I1, b: I2, c: I3, d: I4) => O) ] /** * [Update](#update) pattern consisting of 5 Observables and an accumulrator function. */ export type UpdatePattern5 = [ UpdateTrigger, Property, Property, Property, Property, O | ((acc: O, a: I1, b: I2, c: I3, d: I4, e: I5) => O) ] /** * [Update](#update) pattern consisting of 6 Observables and an accumulrator function. */ export type UpdatePattern6 = [ UpdateTrigger, Property, Property, Property, Property, Property, O | ((acc: O, a: I1, b: I2, c: I3, d: I4, e: I5, f: I6) => O) ] /** * [Update](#update) pattern type, allowing up to 6 sources per pattern. */ export type UpdatePattern = | UpdatePattern1 | UpdatePattern2 | UpdatePattern3 | UpdatePattern4 | UpdatePattern5 | UpdatePattern6 /** Creates a Property from an initial value and updates the value based on multiple inputs. The inputs are defined similarly to [`Bacon.when`](#bacon-when), like this: ```js var result = Bacon.update( initial, [x,y,z, (previous,x,y,z) => { ... }], [x,y, (previous,x,y) => { ... }]) ``` As input, each function above will get the previous value of the `result` Property, along with values from the listed Observables. The value returned by the function will be used as the next value of `result`. Just like in [`Bacon.when`](#when), only EventStreams will trigger an update, while Properties will be just sampled. So, if you list a single EventStream and several Properties, the value will be updated only when an event occurs in the EventStream. Here's a simple gaming example: ```js let scoreMultiplier = Bacon.constant(1) let hitUfo = Bacon.interval(1000) let hitMotherShip = Bacon.later(10000) let score = Bacon.update( 0, [hitUfo, scoreMultiplier, (score, _, multiplier) => score + 100 * multiplier ], [hitMotherShip, (score, _) => score + 2000 ] ) ``` In the example, the `score` property is updated when either `hitUfo` or `hitMotherShip` occur. The `scoreMultiplier` Property is sampled to take multiplier into account when `hitUfo` occurs. * @param initial * @param {UpdatePattern} patterns * @returns {Property} */ export function update( initial: Out, ...patterns: UpdatePattern[] ): PropertySeed export function update( scope: Scope, initial: Out, ...patterns: UpdatePattern[] ): Property export function update(...args: any[]): any { let scope: Scope | undefined let initial: Out let patterns: UpdatePattern[] if (isScope(args[0])) { scope = args[0] initial = args[1] patterns = args.slice(2) } else { scope = undefined initial = args[0] patterns = args.slice(1) } let mutators: EventStreamSeed>[] = patterns.map((pattern) => { if (pattern.length < 2) throw Error(`Illegal pattern ${pattern}, length must be >= 2`) let sources: UpdateParam[] = pattern.slice( 0, pattern.length - 1 ) as any const trigger = sources[0] if (!isEventStreamSeed(trigger)) throw Error(`Illegal pattern ${pattern}, must contain one EventStream`) const properties = sources.slice(1) as Property[] for (let prop of properties) { if (!isPropertySeed(prop)) throw Error( `Illegal pattern ${pattern}. After one EventStream the rest on the observables must be Properties` ) } let combinator = pattern[pattern.length - 1] as (...args: any) => Out if (!(combinator instanceof Function)) { const constantValue = combinator combinator = () => constantValue } return map((v1) => { return (state: Out) => { const propValues = properties.map((p) => p.get()) return combinator(state, v1, ...propValues) } })(trigger as EventStreamSeed) }) return rename( ["update", [initial, patterns]], applyScopeMaybe( scan, Out>(initial, (state, mutation) => mutation(state))( merge(mutators) ), scope ) ) } type Mutation = (prev: V) => V