typescriptrxjs

Making flowing text with RxJS - pass string to another observable


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


Solution

  • 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.