This question is a continuation of my previous question, which you can find here:
How to use RxJS to display the "user enters" indicator?
After successfully checking if the user was typing, I needed to use this particular state as a trigger for the clock.
The logic is simple, in fact, I want the clock to start when the user types. But when the user stops typing, I need the watch to stop. When the user starts typing again, the clock should continue to accumulate.
I already managed to get it working, but it seems like a mess, and I need help refactoring it, so this is not a spaghetti ball. This is what the code looks like:
const showTyping = () => $('.typing').text('User is typing...'); const showIdle = () => $('.typing').text(''); const updateTimer = (x) => $('.timer').text(x); const typing$ = Rx.Observable .fromEvent($('#input'), 'input') .switchMapTo(Rx.Observable .never() .startWith('TYPING') .merge(Rx.Observable.timer(1000).mapTo('IDLE'))) .do(e => e === 'TYPING' ? showTyping() : showIdle()); const timer$ = Rx.Observable .interval(1000) .withLatestFrom(typing$) .map(x => x[1] === 'TYPING' ? 1 : 0) .scan((a, b) => a + b) .do(console.log) .subscribe(updateTimer)
And here is the link to live JSBin: http://jsbin.com/lazeyov/edit?js,console,output
Maybe I'll walk you through the logic of the code:
- First, I create a stream to capture each input event.
- For each of these events, I will use
switchMap to: (a) hide the original TIPING event so that we donβt lose it, and (b) disable the IDLE event, 1 second later. You can see that I create them as separate threads and then combine them together. Thus, I get a stream that will indicate the "input state" in the input field. - I create a second thread that sends an event every second. Using
withLatestFrom , I merge this stream with the previous input stream. Now that they are combined, I can check if the input state is "IDLE" or "TYPING". If they print, I give the event a value of 1 , otherwise 0 . - Now I have stream
1 and 0 s, all I have to do is add them back using .scan() and map it to the DOM.
What is the RxJS way to write this function?
EDIT: Method 1 - Create a Change Event Flow
Based on @osln answer.
const showTyping = () => $('.typing').text('User is typing...'); const showIdle = () => $('.typing').text(''); const updateTimer = (x) => $('.timer').text(x); const handleTypingStateChange = state => state === 1 ? showTyping() : showIdle(); const inputEvents$ = Rx.Observable.fromEvent($('#input'), 'input').share();
JSBin Live Demo
EDIT: Method 2 - Use the outputMap command to start the counter when the user starts typing
Based on Dorus's answer.
const showTyping = () => $('.typing').text('User is typing...'); const showIdle = () => $('.typing').text(''); const updateTimer = (x) => $('.timer').text(x);
JSBin Live Demo