-
-
Save ronag/9f9fdfb7c6169e6850c4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function List(sources) { | |
| const click$ = new Subject() | |
| const actions = intent({ ...sources, click$ }) | |
| const state$ = model(actions).shareReplay(1) | |
| const items$ = state$ | |
| .pluck('items') | |
| ::resolveMap(id => Asset({ | |
| ...sources, | |
| props$: state$ | |
| .pluck('selected') | |
| .map(selected => selected.includes(id)) | |
| .distinctUntilChanged() | |
| .map(selected => ({ id, selected })), | |
| })) | |
| .shareReplay(1) | |
| return { | |
| DOM: view({ | |
| state$, | |
| items$: items$::resolveCombineLatest(item => item.DOM) | |
| }), | |
| HTTP: items$::resolveMerge(item => item.HTTP) | |
| }::using( | |
| () => items$::resolveMerge(item => item.click$).subscribe(click$) | |
| ) | |
| } | |
| export function using(...factories) { | |
| return { | |
| ...this, | |
| DOM: Observable.using(() => new CompositeDisposable(factories.map(f => f())), () => this.DOM), | |
| } | |
| } | |
| export function resolveMap(resolver) { | |
| const cache = new Map() | |
| return this | |
| .startWith([]) | |
| .pairwise() | |
| .map(([prev, curr]) => { | |
| const added = _.difference(curr, prev) | |
| const removed = _.difference(prev, curr) | |
| added.forEach(item => cache.set(item, resolver(item))) | |
| removed.forEach(item => cache.delete(item)) | |
| return curr.map(item => cache.get(item)) | |
| }) | |
| } | |
| export function resolveMerge(resolver) { | |
| const pairs$ = this | |
| .startWith([]) | |
| .pairwise() | |
| .share() | |
| const add$ = pairs$.flatMap(([prev, curr]) => _.difference(curr, prev)) | |
| const remove$ = pairs$.flatMap(([prev, curr]) => _.difference(prev, curr)) | |
| return add$.flatMap(item => resolver(item).takeUntil(remove$.filter(remove => remove === item))) | |
| } | |
| export function resolveCombineLatest(resolver, combiner = (...items) => items) { | |
| return Observable.using( | |
| () => { | |
| const cache = new Map() | |
| cache.dispose = () => cache.forEach(({ subscription }) => subscription.dispose()) | |
| return cache | |
| }, | |
| cache => this | |
| .startWith([]) | |
| .pairwise() | |
| .map(([prev, curr]) => { | |
| const added = _.difference(curr, prev) | |
| const removed = _.difference(prev, curr) | |
| added.forEach(item => { | |
| const subject = new ReplaySubject(1) | |
| cache.set(item, { | |
| value$: subject, | |
| subscription: resolver(item).subscribe(subject), | |
| }) | |
| }) | |
| removed.forEach(item => { | |
| const { subscription } = cache.get(item) | |
| subscription.dispose() | |
| cache.delete(item) | |
| }) | |
| return curr.map(item => cache.get(item).value$) | |
| }) | |
| .flatMapLatest(xs => xs.length | |
| && Observable.combineLatest(...xs, combiner) | |
| || Observable.of(combiner()) | |
| ) | |
| ) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment