I created a function which makes some text flowing:
private createFlowingText(message: string): Observable<string> {
const intrvl = interval(180);
const text = from(message);
const flowTxt = zip(intrvl, text).pipe(
map(([_, letter]) => letter),
scan((acc, letter) => {
const withoutDots = acc.endsWith("...") ? acc.slice(0, -3) : acc;
return `${withoutDots}${letter}...`;
}, ""),
repeat(3)
);
const blinkCursor = interval(400).pipe(
map((i) => (i % 2 == 0 ? "|" : "")),
map((tube) => `${message}...${tube}`)
);
return concat(flowTxt, blinkCursor);
}
I introduced a separate blinkCursor
observable to simulate blinking cursor when text flown three times. And I had to pass a message
value to it. I wonder how can I improve this code without introducing this second observable. So, when the first is finished I switch to the second one and use a single set of operators (passing the resulting text from the first one to the second down the pipeline).
Here's the stackblitz - https://stackblitz.com/edit/stackblitz-starters-jbpdgtzb?file=src%2Fmain.ts
Asked AI to help me and it fixed it with explanations:
private createFlowingText(message: string): Observable<string> {
// fixed with Gemini
const intrvl = interval(180);
const textObservable = from(message);
const flowTxt = zip(intrvl, textObservable).pipe(
map(([_, letter]) => letter),
scan((acc, letter) => {
const withoutDots = acc.endsWith("...") ? acc.slice(0, -3) : acc;
return `${withoutDots}${letter}...`;
}, ""),
repeat(2),
// ShareReplay ensures that when blinkCursor subscribes, it gets the last emitted value
// from flowTxt without re-executing the entire flowTxt observable.
// bufferSize: 1 means it will replay the last 1 value.
// refCount: true means it will automatically connect/disconnect to its source
// when the number of subscribers goes from 0 to 1 or 1 to 0.
shareReplay({ bufferSize: 1, refCount: true })
);
const blinkingTextObservable = flowTxt.pipe(
last(), // Get the very last emitted value from flowTxt
concatMap((finalText) => {
// Now use this final text for the blinking cursor
return interval(400).pipe(
map((i) => (i % 2 == 0 ? "|" : "")),
map((tube) => `${finalText}${tube}`) // Use the finalText here
);
})
);
// Concat flowTxt and then the blinkingTextObservable
return concat(flowTxt, blinkingTextObservable).pipe(takeUntil(this.isLoadingSet$));
}
Nice.